feat: Allow node/edge template per element

* Allow templates to be overwritten per element with template option
This commit is contained in:
Braks
2022-04-10 17:40:27 +02:00
parent 367c7fdf40
commit 88f808711a
9 changed files with 126 additions and 23 deletions

View File

@@ -1,4 +1,5 @@
const baseRules = {
'no-console': 0,
'chai-friendly/no-unused-expressions': ['error', { allowShortCircuit: true, allowTernary: true }],
'prettier/prettier': ['error', {}, { usePrettierrc: true }],
}

View File

@@ -3,14 +3,17 @@
declare global {
const asyncComputed: typeof import('@vueuse/core')['asyncComputed']
const autoResetRef: typeof import('@vueuse/core')['autoResetRef']
const biSyncRef: typeof import('@vueuse/core')['biSyncRef']
const computed: typeof import('vue')['computed']
const computedAsync: typeof import('@vueuse/core')['computedAsync']
const computedEager: typeof import('@vueuse/core')['computedEager']
const computedInject: typeof import('@vueuse/core')['computedInject']
const computedWithControl: typeof import('@vueuse/core')['computedWithControl']
const controlledComputed: typeof import('@vueuse/core')['controlledComputed']
const controlledRef: typeof import('@vueuse/core')['controlledRef']
const createApp: typeof import('vue')['createApp']
const createEventHook: typeof import('@vueuse/core')['createEventHook']
const createGlobalState: typeof import('@vueuse/core')['createGlobalState']
const createInjectionState: typeof import('@vueuse/core')['createInjectionState']
const createReactiveFn: typeof import('@vueuse/core')['createReactiveFn']
const createSharedComposable: typeof import('@vueuse/core')['createSharedComposable']
const createUnrefFn: typeof import('@vueuse/core')['createUnrefFn']
@@ -31,6 +34,9 @@ declare global {
const isDefined: typeof import('@vueuse/core')['isDefined']
const isReadonly: typeof import('vue')['isReadonly']
const isRef: typeof import('vue')['isRef']
const logicAnd: typeof import('@vueuse/core')['logicAnd']
const logicNot: typeof import('@vueuse/core')['logicNot']
const logicOr: typeof import('@vueuse/core')['logicOr']
const makeDestructurable: typeof import('@vueuse/core')['makeDestructurable']
const markRaw: typeof import('vue')['markRaw']
const nextTick: typeof import('vue')['nextTick']
@@ -61,12 +67,17 @@ declare global {
const reactivePick: typeof import('@vueuse/core')['reactivePick']
const readonly: typeof import('vue')['readonly']
const ref: typeof import('vue')['ref']
const refAutoReset: typeof import('@vueuse/core')['refAutoReset']
const refDebounced: typeof import('@vueuse/core')['refDebounced']
const refDefault: typeof import('@vueuse/core')['refDefault']
const refThrottled: typeof import('@vueuse/core')['refThrottled']
const refWithControl: typeof import('@vueuse/core')['refWithControl']
const resolveComponent: typeof import('vue')['resolveComponent']
const shallowReactive: typeof import('vue')['shallowReactive']
const shallowReadonly: typeof import('vue')['shallowReadonly']
const shallowRef: typeof import('vue')['shallowRef']
const syncRef: typeof import('@vueuse/core')['syncRef']
const syncRefs: typeof import('@vueuse/core')['syncRefs']
const templateRef: typeof import('@vueuse/core')['templateRef']
const throttledRef: typeof import('@vueuse/core')['throttledRef']
const throttledWatch: typeof import('@vueuse/core')['throttledWatch']
@@ -75,6 +86,7 @@ declare global {
const toRef: typeof import('vue')['toRef']
const toRefs: typeof import('vue')['toRefs']
const triggerRef: typeof import('vue')['triggerRef']
const tryOnBeforeMount: typeof import('@vueuse/core')['tryOnBeforeMount']
const tryOnBeforeUnmount: typeof import('@vueuse/core')['tryOnBeforeUnmount']
const tryOnMounted: typeof import('@vueuse/core')['tryOnMounted']
const tryOnScopeDispose: typeof import('@vueuse/core')['tryOnScopeDispose']
@@ -102,6 +114,7 @@ declare global {
const useCssVars: typeof import('vue')['useCssVars']
const useCycleList: typeof import('@vueuse/core')['useCycleList']
const useDark: typeof import('@vueuse/core')['useDark']
const useDateFormat: typeof import('@vueuse/core')['useDateFormat']
const useDebounce: typeof import('@vueuse/core')['useDebounce']
const useDebouncedRefHistory: typeof import('@vueuse/core')['useDebouncedRefHistory']
const useDebounceFn: typeof import('@vueuse/core')['useDebounceFn']
@@ -123,10 +136,12 @@ declare global {
const useEyeDropper: typeof import('@vueuse/core')['useEyeDropper']
const useFavicon: typeof import('@vueuse/core')['useFavicon']
const useFetch: typeof import('@vueuse/core')['useFetch']
const useFileSystemAccess: typeof import('@vueuse/core')['useFileSystemAccess']
const useFocus: typeof import('@vueuse/core')['useFocus']
const useFocusWithin: typeof import('@vueuse/core')['useFocusWithin']
const useFps: typeof import('@vueuse/core')['useFps']
const useFullscreen: typeof import('@vueuse/core')['useFullscreen']
const useGamepad: typeof import('@vueuse/core')['useGamepad']
const useGeolocation: typeof import('@vueuse/core')['useGeolocation']
const useIdle: typeof import('@vueuse/core')['useIdle']
const useInfiniteScroll: typeof import('@vueuse/core')['useInfiniteScroll']
@@ -184,6 +199,7 @@ declare global {
const useTimeAgo: typeof import('@vueuse/core')['useTimeAgo']
const useTimeout: typeof import('@vueuse/core')['useTimeout']
const useTimeoutFn: typeof import('@vueuse/core')['useTimeoutFn']
const useTimeoutPoll: typeof import('@vueuse/core')['useTimeoutPoll']
const useTimestamp: typeof import('@vueuse/core')['useTimestamp']
const useTitle: typeof import('@vueuse/core')['useTitle']
const useToggle: typeof import('@vueuse/core')['useToggle']
@@ -204,8 +220,12 @@ declare global {
const useWindowSize: typeof import('@vueuse/core')['useWindowSize']
const watch: typeof import('vue')['watch']
const watchAtMost: typeof import('@vueuse/core')['watchAtMost']
const watchDebounced: typeof import('@vueuse/core')['watchDebounced']
const watchEffect: typeof import('vue')['watchEffect']
const watchIgnorable: typeof import('@vueuse/core')['watchIgnorable']
const watchOnce: typeof import('@vueuse/core')['watchOnce']
const watchPausable: typeof import('@vueuse/core')['watchPausable']
const watchThrottled: typeof import('@vueuse/core')['watchThrottled']
const watchWithFilter: typeof import('@vueuse/core')['watchWithFilter']
const whenever: typeof import('@vueuse/core')['whenever']
}

View File

@@ -271,6 +271,52 @@ const elements = ref([
</template>
```
### Edge Template
You can also set a template per edge, which will overwrite the edge-type component but will retain
the type otherwise.
```vue:no-line-numbers
<script setup>
import { markRaw } from 'vue'
import CustomEdge from './CustomEdge.vue'
const elements = ref([
{
id: '1',
label: 'Node 1',
position: { x: 0, y: 0 },
},
{
id: '2',
label: 'Node 2',
position: { x: 0, y: 150 },
},
{
id: '3',
label: 'Node 3',
position: { x: 0, y: 300 },
},
{
id: 'e1-2',
source: '1',
target: '2',
},
{
id: 'e1-3',
source: '1',
target: '2',
template: markRaw(CustomEdge),
},
])
</script>
<template>
<div style="height: 300px">
<VueFlow v-model="elements" />
</div>
</template>
```
### Custom Edge Props
Your custom edges are wrapped so that the basic functions like selecting work.

View File

@@ -266,6 +266,44 @@ const elements = ref([
You can find a more advanced example <router-link to="/examples/custom-node/">here</router-link>.
:::
### Node Template
You can also set a template per node, which will overwrite the node-type component but will retain
the type otherwise.
```vue:no-line-numbers
<script setup>
import { markRaw } from 'vue'
import CustomNode from './CustomNode.vue'
import CustomNode from './OverwriteCustomNode.vue'
import SpecialNode from './SpecialNode.vue'
const nodeTypes = {
custom: markRaw(CustomNode),
special: markRaw(SpecialNode),
}
const elements = ref([
{
id: '1',
label: 'Node 1',
type: 'custom',
template: markRaw(OverwriteCustomNode),
},
{
id: '1',
label: 'Node 1',
type: 'special',
}
])
</script>
<template>
<div style="height: 300px">
<VueFlow v-model="elements" :node-types="nodeTypes" />
</div>
</template>
```
### Custom Node Props
Your custom nodes are wrapped so that the basic functions like dragging or selecting work.

View File

@@ -124,8 +124,9 @@ watch(
)
const type = computed(() => {
let edgeType = edge.value.template ?? store.getEdgeTypes[name.value]
const instance = getCurrentInstance()
let edgeType = store.getEdgeTypes[name.value]
if (typeof edgeType === 'string') {
if (instance) {
const components = Object.keys(instance.appContext.components)

View File

@@ -1,6 +1,6 @@
<script lang="ts" setup>
import { useDraggableCore } from '@braks/revue-draggable'
import { CSSProperties } from 'vue'
import { CSSProperties, isVNode } from 'vue'
import { useVueFlow } from '../../composables'
import { GraphNode, NodeComponent, SnapGrid } from '../../types'
import { NodeId, Slots } from '../../context'
@@ -89,8 +89,9 @@ watch(
)
const type = computed(() => {
let nodeType = node.value.template ?? store.getNodeTypes[name.value]
const instance = getCurrentInstance()
let nodeType = store.getNodeTypes[name.value]
if (typeof nodeType === 'string') {
if (instance) {
const components = Object.keys(instance.appContext.components)

View File

@@ -1,6 +1,7 @@
import { CSSProperties } from 'vue'
import { Component, CSSProperties, VNode } from 'vue'
import { Position, Element, ElementData } from './flow'
import { GraphNode } from './node'
import { EdgeComponent, EdgeTextProps } from './components'
/** Edge markers */
export enum MarkerType {
@@ -42,6 +43,7 @@ export interface MarkerProps {
export type EdgeMarkerType = string | MarkerType | EdgeMarker
export interface Edge<Data = ElementData> extends Element<Data> {
label?: string | VNode | Component<EdgeTextProps>
/** Source node id */
source: string
/** Target node id */
@@ -74,6 +76,9 @@ export interface Edge<Data = ElementData> extends Element<Data> {
updatable?: boolean
/** Disable/enable selecting edge */
selectable?: boolean
/** overwrites current edge type */
template?: EdgeComponent
}
export type DefaultEdgeOptions = Omit<
@@ -101,12 +106,7 @@ export interface EdgeProps<Data = ElementData, SourceNodeData = any, TargetNodeD
id: string
sourceNode: GraphNode<SourceNodeData>
targetNode: GraphNode<TargetNodeData>
label?:
| string
| {
props?: any
component: any
}
label?: string | VNode
type?: string
data?: Data
style?: CSSProperties
@@ -139,12 +139,7 @@ export interface SmoothStepEdgeProps<Data = ElementData, SourceNodeData = any, T
id: string
sourceNode: GraphNode<SourceNodeData>
targetNode: GraphNode<TargetNodeData>
label?:
| string
| {
props?: any
component: any
}
label?: string | VNode
type?: string
data?: Data
style?: CSSProperties
@@ -159,8 +154,6 @@ export interface SmoothStepEdgeProps<Data = ElementData, SourceNodeData = any, T
targetHandleId?: string
source: string
target: string
sourceHandle?: string
targetHandle?: string
labelStyle?: any
labelShowBg?: boolean
labelBgStyle?: any

View File

@@ -1,4 +1,4 @@
import { CSSProperties, VNode } from 'vue'
import { Component, CSSProperties, VNode } from 'vue'
import { GraphEdge, Edge, DefaultEdgeOptions } from './edge'
import { GraphNode, CoordinateExtent, Node } from './node'
import { ConnectionLineType, ConnectionMode } from './connection'
@@ -27,7 +27,7 @@ export type StyleFunc<Data = ElementData> = (element: FlowElement<Data>) => Styl
/** base element props */
export interface Element<Data extends ElementData = ElementData> {
id: string
label?: string | VNode
label?: string | VNode | Component
type?: string
data?: Data
class?: string | ClassFunc<Data>

View File

@@ -1,6 +1,6 @@
import { VNode } from 'vue'
import { Component, VNode } from 'vue'
import { XYPosition, Position, SnapGrid, Element, XYZPosition, Dimensions, ElementData } from './flow'
import { DefaultNodeTypes } from './components'
import { DefaultNodeTypes, NodeComponent } from './components'
import { HandleElement, ValidConnectionFunc } from './handle'
/** Defined as [[x-from, y-from], [x-to, y-to]] **/
@@ -55,6 +55,9 @@ export interface Node<Data = ElementData> extends Element<Data> {
* or pass a string with units (height: `10rem` -> height: 10rem)
*/
height?: number | string | HeightFunc
/** overwrites current node type */
template?: NodeComponent
}
export interface GraphNode<Data = ElementData> extends Node<Data> {