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-11 11:30:10 +02:00
parent 367c7fdf40
commit 88f808711a
9 changed files with 126 additions and 23 deletions
+1
View File
@@ -1,4 +1,5 @@
const baseRules = { const baseRules = {
'no-console': 0,
'chai-friendly/no-unused-expressions': ['error', { allowShortCircuit: true, allowTernary: true }], 'chai-friendly/no-unused-expressions': ['error', { allowShortCircuit: true, allowTernary: true }],
'prettier/prettier': ['error', {}, { usePrettierrc: true }], 'prettier/prettier': ['error', {}, { usePrettierrc: true }],
} }
+21 -1
View File
@@ -3,14 +3,17 @@
declare global { declare global {
const asyncComputed: typeof import('@vueuse/core')['asyncComputed'] const asyncComputed: typeof import('@vueuse/core')['asyncComputed']
const autoResetRef: typeof import('@vueuse/core')['autoResetRef'] const autoResetRef: typeof import('@vueuse/core')['autoResetRef']
const biSyncRef: typeof import('@vueuse/core')['biSyncRef']
const computed: typeof import('vue')['computed'] 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 computedInject: typeof import('@vueuse/core')['computedInject']
const computedWithControl: typeof import('@vueuse/core')['computedWithControl']
const controlledComputed: typeof import('@vueuse/core')['controlledComputed'] const controlledComputed: typeof import('@vueuse/core')['controlledComputed']
const controlledRef: typeof import('@vueuse/core')['controlledRef'] const controlledRef: typeof import('@vueuse/core')['controlledRef']
const createApp: typeof import('vue')['createApp'] const createApp: typeof import('vue')['createApp']
const createEventHook: typeof import('@vueuse/core')['createEventHook'] const createEventHook: typeof import('@vueuse/core')['createEventHook']
const createGlobalState: typeof import('@vueuse/core')['createGlobalState'] const createGlobalState: typeof import('@vueuse/core')['createGlobalState']
const createInjectionState: typeof import('@vueuse/core')['createInjectionState']
const createReactiveFn: typeof import('@vueuse/core')['createReactiveFn'] const createReactiveFn: typeof import('@vueuse/core')['createReactiveFn']
const createSharedComposable: typeof import('@vueuse/core')['createSharedComposable'] const createSharedComposable: typeof import('@vueuse/core')['createSharedComposable']
const createUnrefFn: typeof import('@vueuse/core')['createUnrefFn'] const createUnrefFn: typeof import('@vueuse/core')['createUnrefFn']
@@ -31,6 +34,9 @@ declare global {
const isDefined: typeof import('@vueuse/core')['isDefined'] const isDefined: typeof import('@vueuse/core')['isDefined']
const isReadonly: typeof import('vue')['isReadonly'] const isReadonly: typeof import('vue')['isReadonly']
const isRef: typeof import('vue')['isRef'] 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 makeDestructurable: typeof import('@vueuse/core')['makeDestructurable']
const markRaw: typeof import('vue')['markRaw'] const markRaw: typeof import('vue')['markRaw']
const nextTick: typeof import('vue')['nextTick'] const nextTick: typeof import('vue')['nextTick']
@@ -61,12 +67,17 @@ declare global {
const reactivePick: typeof import('@vueuse/core')['reactivePick'] const reactivePick: typeof import('@vueuse/core')['reactivePick']
const readonly: typeof import('vue')['readonly'] const readonly: typeof import('vue')['readonly']
const ref: typeof import('vue')['ref'] 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 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 resolveComponent: typeof import('vue')['resolveComponent']
const shallowReactive: typeof import('vue')['shallowReactive'] const shallowReactive: typeof import('vue')['shallowReactive']
const shallowReadonly: typeof import('vue')['shallowReadonly'] const shallowReadonly: typeof import('vue')['shallowReadonly']
const shallowRef: typeof import('vue')['shallowRef'] const shallowRef: typeof import('vue')['shallowRef']
const syncRef: typeof import('@vueuse/core')['syncRef'] const syncRef: typeof import('@vueuse/core')['syncRef']
const syncRefs: typeof import('@vueuse/core')['syncRefs']
const templateRef: typeof import('@vueuse/core')['templateRef'] const templateRef: typeof import('@vueuse/core')['templateRef']
const throttledRef: typeof import('@vueuse/core')['throttledRef'] const throttledRef: typeof import('@vueuse/core')['throttledRef']
const throttledWatch: typeof import('@vueuse/core')['throttledWatch'] const throttledWatch: typeof import('@vueuse/core')['throttledWatch']
@@ -75,6 +86,7 @@ declare global {
const toRef: typeof import('vue')['toRef'] const toRef: typeof import('vue')['toRef']
const toRefs: typeof import('vue')['toRefs'] const toRefs: typeof import('vue')['toRefs']
const triggerRef: typeof import('vue')['triggerRef'] const triggerRef: typeof import('vue')['triggerRef']
const tryOnBeforeMount: typeof import('@vueuse/core')['tryOnBeforeMount']
const tryOnBeforeUnmount: typeof import('@vueuse/core')['tryOnBeforeUnmount'] const tryOnBeforeUnmount: typeof import('@vueuse/core')['tryOnBeforeUnmount']
const tryOnMounted: typeof import('@vueuse/core')['tryOnMounted'] const tryOnMounted: typeof import('@vueuse/core')['tryOnMounted']
const tryOnScopeDispose: typeof import('@vueuse/core')['tryOnScopeDispose'] const tryOnScopeDispose: typeof import('@vueuse/core')['tryOnScopeDispose']
@@ -102,6 +114,7 @@ declare global {
const useCssVars: typeof import('vue')['useCssVars'] const useCssVars: typeof import('vue')['useCssVars']
const useCycleList: typeof import('@vueuse/core')['useCycleList'] const useCycleList: typeof import('@vueuse/core')['useCycleList']
const useDark: typeof import('@vueuse/core')['useDark'] const useDark: typeof import('@vueuse/core')['useDark']
const useDateFormat: typeof import('@vueuse/core')['useDateFormat']
const useDebounce: typeof import('@vueuse/core')['useDebounce'] const useDebounce: typeof import('@vueuse/core')['useDebounce']
const useDebouncedRefHistory: typeof import('@vueuse/core')['useDebouncedRefHistory'] const useDebouncedRefHistory: typeof import('@vueuse/core')['useDebouncedRefHistory']
const useDebounceFn: typeof import('@vueuse/core')['useDebounceFn'] const useDebounceFn: typeof import('@vueuse/core')['useDebounceFn']
@@ -123,10 +136,12 @@ declare global {
const useEyeDropper: typeof import('@vueuse/core')['useEyeDropper'] const useEyeDropper: typeof import('@vueuse/core')['useEyeDropper']
const useFavicon: typeof import('@vueuse/core')['useFavicon'] const useFavicon: typeof import('@vueuse/core')['useFavicon']
const useFetch: typeof import('@vueuse/core')['useFetch'] const useFetch: typeof import('@vueuse/core')['useFetch']
const useFileSystemAccess: typeof import('@vueuse/core')['useFileSystemAccess']
const useFocus: typeof import('@vueuse/core')['useFocus'] const useFocus: typeof import('@vueuse/core')['useFocus']
const useFocusWithin: typeof import('@vueuse/core')['useFocusWithin'] const useFocusWithin: typeof import('@vueuse/core')['useFocusWithin']
const useFps: typeof import('@vueuse/core')['useFps'] const useFps: typeof import('@vueuse/core')['useFps']
const useFullscreen: typeof import('@vueuse/core')['useFullscreen'] const useFullscreen: typeof import('@vueuse/core')['useFullscreen']
const useGamepad: typeof import('@vueuse/core')['useGamepad']
const useGeolocation: typeof import('@vueuse/core')['useGeolocation'] const useGeolocation: typeof import('@vueuse/core')['useGeolocation']
const useIdle: typeof import('@vueuse/core')['useIdle'] const useIdle: typeof import('@vueuse/core')['useIdle']
const useInfiniteScroll: typeof import('@vueuse/core')['useInfiniteScroll'] const useInfiniteScroll: typeof import('@vueuse/core')['useInfiniteScroll']
@@ -184,6 +199,7 @@ declare global {
const useTimeAgo: typeof import('@vueuse/core')['useTimeAgo'] const useTimeAgo: typeof import('@vueuse/core')['useTimeAgo']
const useTimeout: typeof import('@vueuse/core')['useTimeout'] const useTimeout: typeof import('@vueuse/core')['useTimeout']
const useTimeoutFn: typeof import('@vueuse/core')['useTimeoutFn'] const useTimeoutFn: typeof import('@vueuse/core')['useTimeoutFn']
const useTimeoutPoll: typeof import('@vueuse/core')['useTimeoutPoll']
const useTimestamp: typeof import('@vueuse/core')['useTimestamp'] const useTimestamp: typeof import('@vueuse/core')['useTimestamp']
const useTitle: typeof import('@vueuse/core')['useTitle'] const useTitle: typeof import('@vueuse/core')['useTitle']
const useToggle: typeof import('@vueuse/core')['useToggle'] const useToggle: typeof import('@vueuse/core')['useToggle']
@@ -204,8 +220,12 @@ declare global {
const useWindowSize: typeof import('@vueuse/core')['useWindowSize'] const useWindowSize: typeof import('@vueuse/core')['useWindowSize']
const watch: typeof import('vue')['watch'] const watch: typeof import('vue')['watch']
const watchAtMost: typeof import('@vueuse/core')['watchAtMost'] const watchAtMost: typeof import('@vueuse/core')['watchAtMost']
const watchDebounced: typeof import('@vueuse/core')['watchDebounced']
const watchEffect: typeof import('vue')['watchEffect'] const watchEffect: typeof import('vue')['watchEffect']
const watchIgnorable: typeof import('@vueuse/core')['watchIgnorable']
const watchOnce: typeof import('@vueuse/core')['watchOnce'] 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 watchWithFilter: typeof import('@vueuse/core')['watchWithFilter']
const whenever: typeof import('@vueuse/core')['whenever'] const whenever: typeof import('@vueuse/core')['whenever']
} }
+46
View File
@@ -271,6 +271,52 @@ const elements = ref([
</template> </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 ### Custom Edge Props
Your custom edges are wrapped so that the basic functions like selecting work. Your custom edges are wrapped so that the basic functions like selecting work.
+38
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>. 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 ### Custom Node Props
Your custom nodes are wrapped so that the basic functions like dragging or selecting work. Your custom nodes are wrapped so that the basic functions like dragging or selecting work.
+2 -1
View File
@@ -124,8 +124,9 @@ watch(
) )
const type = computed(() => { const type = computed(() => {
let edgeType = edge.value.template ?? store.getEdgeTypes[name.value]
const instance = getCurrentInstance() const instance = getCurrentInstance()
let edgeType = store.getEdgeTypes[name.value]
if (typeof edgeType === 'string') { if (typeof edgeType === 'string') {
if (instance) { if (instance) {
const components = Object.keys(instance.appContext.components) const components = Object.keys(instance.appContext.components)
+3 -2
View File
@@ -1,6 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import { useDraggableCore } from '@braks/revue-draggable' import { useDraggableCore } from '@braks/revue-draggable'
import { CSSProperties } from 'vue' import { CSSProperties, isVNode } from 'vue'
import { useVueFlow } from '../../composables' import { useVueFlow } from '../../composables'
import { GraphNode, NodeComponent, SnapGrid } from '../../types' import { GraphNode, NodeComponent, SnapGrid } from '../../types'
import { NodeId, Slots } from '../../context' import { NodeId, Slots } from '../../context'
@@ -89,8 +89,9 @@ watch(
) )
const type = computed(() => { const type = computed(() => {
let nodeType = node.value.template ?? store.getNodeTypes[name.value]
const instance = getCurrentInstance() const instance = getCurrentInstance()
let nodeType = store.getNodeTypes[name.value]
if (typeof nodeType === 'string') { if (typeof nodeType === 'string') {
if (instance) { if (instance) {
const components = Object.keys(instance.appContext.components) const components = Object.keys(instance.appContext.components)
+8 -15
View File
@@ -1,6 +1,7 @@
import { CSSProperties } from 'vue' import { Component, CSSProperties, VNode } from 'vue'
import { Position, Element, ElementData } from './flow' import { Position, Element, ElementData } from './flow'
import { GraphNode } from './node' import { GraphNode } from './node'
import { EdgeComponent, EdgeTextProps } from './components'
/** Edge markers */ /** Edge markers */
export enum MarkerType { export enum MarkerType {
@@ -42,6 +43,7 @@ export interface MarkerProps {
export type EdgeMarkerType = string | MarkerType | EdgeMarker export type EdgeMarkerType = string | MarkerType | EdgeMarker
export interface Edge<Data = ElementData> extends Element<Data> { export interface Edge<Data = ElementData> extends Element<Data> {
label?: string | VNode | Component<EdgeTextProps>
/** Source node id */ /** Source node id */
source: string source: string
/** Target node id */ /** Target node id */
@@ -74,6 +76,9 @@ export interface Edge<Data = ElementData> extends Element<Data> {
updatable?: boolean updatable?: boolean
/** Disable/enable selecting edge */ /** Disable/enable selecting edge */
selectable?: boolean selectable?: boolean
/** overwrites current edge type */
template?: EdgeComponent
} }
export type DefaultEdgeOptions = Omit< export type DefaultEdgeOptions = Omit<
@@ -101,12 +106,7 @@ export interface EdgeProps<Data = ElementData, SourceNodeData = any, TargetNodeD
id: string id: string
sourceNode: GraphNode<SourceNodeData> sourceNode: GraphNode<SourceNodeData>
targetNode: GraphNode<TargetNodeData> targetNode: GraphNode<TargetNodeData>
label?: label?: string | VNode
| string
| {
props?: any
component: any
}
type?: string type?: string
data?: Data data?: Data
style?: CSSProperties style?: CSSProperties
@@ -139,12 +139,7 @@ export interface SmoothStepEdgeProps<Data = ElementData, SourceNodeData = any, T
id: string id: string
sourceNode: GraphNode<SourceNodeData> sourceNode: GraphNode<SourceNodeData>
targetNode: GraphNode<TargetNodeData> targetNode: GraphNode<TargetNodeData>
label?: label?: string | VNode
| string
| {
props?: any
component: any
}
type?: string type?: string
data?: Data data?: Data
style?: CSSProperties style?: CSSProperties
@@ -159,8 +154,6 @@ export interface SmoothStepEdgeProps<Data = ElementData, SourceNodeData = any, T
targetHandleId?: string targetHandleId?: string
source: string source: string
target: string target: string
sourceHandle?: string
targetHandle?: string
labelStyle?: any labelStyle?: any
labelShowBg?: boolean labelShowBg?: boolean
labelBgStyle?: any labelBgStyle?: any
+2 -2
View File
@@ -1,4 +1,4 @@
import { CSSProperties, VNode } from 'vue' import { Component, CSSProperties, VNode } from 'vue'
import { GraphEdge, Edge, DefaultEdgeOptions } from './edge' import { GraphEdge, Edge, DefaultEdgeOptions } from './edge'
import { GraphNode, CoordinateExtent, Node } from './node' import { GraphNode, CoordinateExtent, Node } from './node'
import { ConnectionLineType, ConnectionMode } from './connection' import { ConnectionLineType, ConnectionMode } from './connection'
@@ -27,7 +27,7 @@ export type StyleFunc<Data = ElementData> = (element: FlowElement<Data>) => Styl
/** base element props */ /** base element props */
export interface Element<Data extends ElementData = ElementData> { export interface Element<Data extends ElementData = ElementData> {
id: string id: string
label?: string | VNode label?: string | VNode | Component
type?: string type?: string
data?: Data data?: Data
class?: string | ClassFunc<Data> class?: string | ClassFunc<Data>
+5 -2
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 { XYPosition, Position, SnapGrid, Element, XYZPosition, Dimensions, ElementData } from './flow'
import { DefaultNodeTypes } from './components' import { DefaultNodeTypes, NodeComponent } from './components'
import { HandleElement, ValidConnectionFunc } from './handle' import { HandleElement, ValidConnectionFunc } from './handle'
/** Defined as [[x-from, y-from], [x-to, y-to]] **/ /** 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) * or pass a string with units (height: `10rem` -> height: 10rem)
*/ */
height?: number | string | HeightFunc height?: number | string | HeightFunc
/** overwrites current node type */
template?: NodeComponent
} }
export interface GraphNode<Data = ElementData> extends Node<Data> { export interface GraphNode<Data = ElementData> extends Node<Data> {