chore(docs): update example styles (#1469)
* chore(docs): update basic example styles * chore(docs): update confirm delete example styles * chore(docs): update dnd example styles * chore(docs): update hidden example * chore(docs): update update node example * chore(docs): update node toolbar example styles * chore(docs): update transition example styles * chore(docs): cleanup basic example els * chore(docs): cleanup examples
This commit is contained in:
+13
-10
@@ -8,10 +8,12 @@ import { initialEdges, initialNodes } from './initial-elements.js'
|
|||||||
import Icon from './Icon.vue'
|
import Icon from './Icon.vue'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* useVueFlow provides all event handlers and store properties
|
* `useVueFlow` provides:
|
||||||
* You can pass the composable an object that has the same properties as the VueFlow component props
|
* 1. a set of methods to interact with the VueFlow instance (like `fitView`, `setViewport`, `addEdges`, etc)
|
||||||
|
* 2. a set of event-hooks to listen to VueFlow events (like `onInit`, `onNodeDragStop`, `onConnect`, etc)
|
||||||
|
* 3. the internal state of the VueFlow instance (like `nodes`, `edges`, `viewport`, etc)
|
||||||
*/
|
*/
|
||||||
const { onPaneReady, onNodeDragStop, onConnect, addEdges, setViewport, toObject } = useVueFlow()
|
const { onInit, onNodeDragStop, onConnect, addEdges, setViewport, toObject } = useVueFlow()
|
||||||
|
|
||||||
const nodes = ref(initialNodes)
|
const nodes = ref(initialNodes)
|
||||||
|
|
||||||
@@ -24,10 +26,11 @@ const dark = ref(false)
|
|||||||
* This is a Vue Flow event-hook which can be listened to from anywhere you call the composable, instead of only on the main component
|
* This is a Vue Flow event-hook which can be listened to from anywhere you call the composable, instead of only on the main component
|
||||||
* Any event that is available as `@event-name` on the VueFlow component is also available as `onEventName` on the composable and vice versa
|
* Any event that is available as `@event-name` on the VueFlow component is also available as `onEventName` on the composable and vice versa
|
||||||
*
|
*
|
||||||
* onPaneReady is called when viewpane & nodes have visible dimensions
|
* onInit is called when the VueFlow viewport is initialized
|
||||||
*/
|
*/
|
||||||
onPaneReady(({ fitView }) => {
|
onInit((vueFlowInstance) => {
|
||||||
fitView()
|
// instance is the same as the return of `useVueFlow`
|
||||||
|
vueFlowInstance.fitView()
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -39,8 +42,8 @@ onPaneReady(({ fitView }) => {
|
|||||||
* 3. the node that initiated the drag
|
* 3. the node that initiated the drag
|
||||||
* 4. any intersections with other nodes
|
* 4. any intersections with other nodes
|
||||||
*/
|
*/
|
||||||
onNodeDragStop(({ event, nodes, node, intersections }) => {
|
onNodeDragStop(({ event, nodes, node }) => {
|
||||||
console.log('Node Drag Stop', { event, nodes, node, intersections })
|
console.log('Node Drag Stop', { event, nodes, node })
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -94,7 +97,7 @@ function toggleDarkMode() {
|
|||||||
:nodes="nodes"
|
:nodes="nodes"
|
||||||
:edges="edges"
|
:edges="edges"
|
||||||
:class="{ dark }"
|
:class="{ dark }"
|
||||||
class="basicflow"
|
class="basic-flow"
|
||||||
:default-viewport="{ zoom: 1.5 }"
|
:default-viewport="{ zoom: 1.5 }"
|
||||||
:min-zoom="0.2"
|
:min-zoom="0.2"
|
||||||
:max-zoom="4"
|
:max-zoom="4"
|
||||||
@@ -103,7 +106,7 @@ function toggleDarkMode() {
|
|||||||
|
|
||||||
<MiniMap />
|
<MiniMap />
|
||||||
|
|
||||||
<Controls position="top-right">
|
<Controls position="top-left">
|
||||||
<ControlButton title="Reset Transform" @click="resetTransform">
|
<ControlButton title="Reset Transform" @click="resetTransform">
|
||||||
<Icon name="reset" />
|
<Icon name="reset" />
|
||||||
</ControlButton>
|
</ControlButton>
|
||||||
|
|||||||
@@ -1,24 +1,69 @@
|
|||||||
import { MarkerType } from '@vue-flow/core'
|
import { MarkerType } from '@vue-flow/core'
|
||||||
|
|
||||||
export const initialNodes = [
|
export const initialNodes = [
|
||||||
{ id: '1', type: 'input', label: 'Node 1', position: { x: 250, y: 0 }, class: 'light' },
|
{
|
||||||
{ id: '2', type: 'output', label: 'Node 2', position: { x: 100, y: 100 }, class: 'light' },
|
id: '1',
|
||||||
{ id: '3', label: 'Node 3', position: { x: 400, y: 100 }, class: 'light' },
|
type: 'input',
|
||||||
{ id: '4', label: 'Node 4', position: { x: 150, y: 200 }, class: 'light' },
|
data: { label: 'Node 1' },
|
||||||
{ id: '5', type: 'output', label: 'Node 5', position: { x: 300, y: 300 }, class: 'light' },
|
position: { x: 250, y: 0 },
|
||||||
|
class: 'light',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
type: 'output',
|
||||||
|
data: { label: 'Node 2' },
|
||||||
|
position: { x: 100, y: 100 },
|
||||||
|
class: 'light',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '3',
|
||||||
|
data: { label: 'Node 3' },
|
||||||
|
position: { x: 400, y: 100 },
|
||||||
|
class: 'light',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '4',
|
||||||
|
data: { label: 'Node 4' },
|
||||||
|
position: { x: 150, y: 200 },
|
||||||
|
class: 'light',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '5',
|
||||||
|
type: 'output',
|
||||||
|
data: { label: 'Node 5' },
|
||||||
|
position: { x: 300, y: 300 },
|
||||||
|
class: 'light',
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
export const initialEdges = [
|
export const initialEdges = [
|
||||||
{ id: 'e1-2', source: '1', target: '2', animated: true },
|
{
|
||||||
{ id: 'e1-3', label: 'edge with arrowhead', source: '1', target: '3', markerEnd: MarkerType.ArrowClosed },
|
id: 'e1-2',
|
||||||
|
source: '1',
|
||||||
|
target: '2',
|
||||||
|
animated: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'e1-3',
|
||||||
|
source: '1',
|
||||||
|
target: '3',
|
||||||
|
label: 'edge with arrowhead',
|
||||||
|
markerEnd: MarkerType.ArrowClosed,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'e4-5',
|
id: 'e4-5',
|
||||||
type: 'step',
|
type: 'step',
|
||||||
label: 'step-edge',
|
|
||||||
source: '4',
|
source: '4',
|
||||||
target: '5',
|
target: '5',
|
||||||
|
label: 'Node 2',
|
||||||
style: { stroke: 'orange' },
|
style: { stroke: 'orange' },
|
||||||
labelBgStyle: { fill: 'orange' },
|
labelBgStyle: { fill: 'orange' },
|
||||||
},
|
},
|
||||||
{ id: 'e3-4', type: 'smoothstep', label: 'smoothstep-edge', source: '3', target: '4' },
|
{
|
||||||
|
id: 'e3-4',
|
||||||
|
type: 'smoothstep',
|
||||||
|
source: '3',
|
||||||
|
target: '4',
|
||||||
|
label: 'smoothstep-edge',
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,48 +1,52 @@
|
|||||||
.basicflow.dark {
|
.basic-flow.dark {
|
||||||
background: #000000;
|
background: #2d3748;
|
||||||
color: #FFFFFB;
|
color: #FFFFFB;
|
||||||
}
|
}
|
||||||
|
|
||||||
.basicflow.dark .vue-flow__node {
|
.basic-flow.dark .vue-flow__node {
|
||||||
background: hsl(0, 0%, 10%);
|
background: #4a5568;
|
||||||
color: #FFFFFB;
|
color: #FFFFFB;
|
||||||
}
|
}
|
||||||
|
|
||||||
.basicflow.dark .vue-flow__node.selected {
|
.basic-flow.dark .vue-flow__node.selected {
|
||||||
background: hsl(0, 0%, 20%);
|
background: hsl(0, 0%, 20%);
|
||||||
border: 1px solid hotpink;
|
box-shadow: 0 0 0 2px #2563eb;
|
||||||
}
|
}
|
||||||
|
|
||||||
.basicflow .vue-flow__controls {
|
.basic-flow .vue-flow__controls {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.basicflow.dark .vue-flow__controls {
|
.basic-flow.dark .vue-flow__controls {
|
||||||
border: 1px solid #FFFFFB;
|
border: 1px solid #FFFFFB;
|
||||||
}
|
}
|
||||||
|
|
||||||
.basicflow .vue-flow__controls .vue-flow__controls-button {
|
.basic-flow .vue-flow__controls .vue-flow__controls-button {
|
||||||
border: none;
|
border: none;
|
||||||
border-right: 1px solid #eee;
|
border-right: 1px solid #eee;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.basic-flow .vue-flow__controls .vue-flow__controls-button svg {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.basicflow.dark .vue-flow__controls .vue-flow__controls-button {
|
.basic-flow.dark .vue-flow__controls .vue-flow__controls-button {
|
||||||
background: hsl(0, 0%, 20%);
|
background: hsl(0, 0%, 20%);
|
||||||
fill: #FFFFFB;
|
fill: #FFFFFB;
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.basicflow.dark .vue-flow__controls .vue-flow__controls-button:hover {
|
.basic-flow.dark .vue-flow__controls .vue-flow__controls-button:hover {
|
||||||
background: hsl(0, 0%, 30%);
|
background: hsl(0, 0%, 30%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.basicflow.dark .vue-flow__edge-textbg {
|
.basic-flow.dark .vue-flow__edge-textbg {
|
||||||
fill: #292524;
|
fill: #292524;
|
||||||
}
|
}
|
||||||
|
|
||||||
.basicflow.dark .vue-flow__edge-text {
|
.basic-flow.dark .vue-flow__edge-text {
|
||||||
fill: #FFFFFB;
|
fill: #FFFFFB;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from 'vue'
|
import { h, ref } from 'vue'
|
||||||
import { VueFlow, useVueFlow } from '@vue-flow/core'
|
import { VueFlow, useVueFlow } from '@vue-flow/core'
|
||||||
import { Background } from '@vue-flow/background'
|
import { Background } from '@vue-flow/background'
|
||||||
import { useDialog } from './useDialog'
|
import { useDialog } from './useDialog'
|
||||||
@@ -10,16 +10,26 @@ const { onConnect, addEdges, onNodesChange, onEdgesChange, applyNodeChanges, app
|
|||||||
const dialog = useDialog()
|
const dialog = useDialog()
|
||||||
|
|
||||||
const nodes = ref([
|
const nodes = ref([
|
||||||
{ id: '1', type: 'input', label: 'Node 1', position: { x: 250, y: 5 }, class: 'light' },
|
{ id: '1', type: 'input', data: { label: 'Click me and' }, position: { x: 0, y: 0 } },
|
||||||
{ id: '2', label: 'Node 2', position: { x: 100, y: 100 }, class: 'light' },
|
{ id: '2', data: { label: `press 'Backspace' to delete me` }, position: { x: 0, y: 100 } },
|
||||||
{ id: '3', label: 'Node 3', position: { x: 400, y: 100 }, class: 'light' },
|
|
||||||
{ id: '4', label: 'Node 4', position: { x: 400, y: 200 }, class: 'light' },
|
|
||||||
])
|
])
|
||||||
|
|
||||||
const edges = ref([
|
const edges = ref([{ id: 'e1-2', source: '1', target: '2' }])
|
||||||
{ id: 'e1-2', source: '1', target: '2', animated: true },
|
|
||||||
{ id: 'e1-3', source: '1', target: '3' },
|
function dialogMsg(id) {
|
||||||
])
|
return h(
|
||||||
|
'span',
|
||||||
|
{
|
||||||
|
style: {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: '8px',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[`Are you sure?`, h('br'), h('span', `[ELEMENT_ID: ${id}]`)],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
onConnect(addEdges)
|
onConnect(addEdges)
|
||||||
|
|
||||||
@@ -28,7 +38,7 @@ onNodesChange(async (changes) => {
|
|||||||
|
|
||||||
for (const change of changes) {
|
for (const change of changes) {
|
||||||
if (change.type === 'remove') {
|
if (change.type === 'remove') {
|
||||||
const isConfirmed = await dialog.confirm(`Do you really want to delete this node: ${change.id}?`)
|
const isConfirmed = await dialog.confirm(dialogMsg(change.id))
|
||||||
|
|
||||||
if (isConfirmed) {
|
if (isConfirmed) {
|
||||||
nextChanges.push(change)
|
nextChanges.push(change)
|
||||||
@@ -46,7 +56,7 @@ onEdgesChange(async (changes) => {
|
|||||||
|
|
||||||
for (const change of changes) {
|
for (const change of changes) {
|
||||||
if (change.type === 'remove') {
|
if (change.type === 'remove') {
|
||||||
const isConfirmed = await dialog.confirm(`Do you really want to delete this edge: ${change.id}?`)
|
const isConfirmed = await dialog.confirm(dialogMsg(change.id))
|
||||||
|
|
||||||
if (isConfirmed) {
|
if (isConfirmed) {
|
||||||
nextChanges.push(change)
|
nextChanges.push(change)
|
||||||
@@ -61,7 +71,7 @@ onEdgesChange(async (changes) => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<VueFlow :nodes="nodes" :edges="edges" :apply-default="false" fit-view-on-init class="vue-flow-basic-example">
|
<VueFlow :nodes="nodes" :edges="edges" :apply-default="false" fit-view-on-init class="confirm-flow">
|
||||||
<Background />
|
<Background />
|
||||||
|
|
||||||
<Dialog />
|
<Dialog />
|
||||||
|
|||||||
@@ -17,9 +17,17 @@ function cancel() {
|
|||||||
<template>
|
<template>
|
||||||
<div v-if="isVisible" class="dialog-overlay">
|
<div v-if="isVisible" class="dialog-overlay">
|
||||||
<div class="dialog">
|
<div class="dialog">
|
||||||
<p>{{ message }}</p>
|
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24">
|
||||||
|
<path
|
||||||
|
fill="#e53e3e"
|
||||||
|
d="M7 21q-.825 0-1.412-.587T5 19V6H4V4h5V3h6v1h5v2h-1v13q0 .825-.587 1.413T17 21zm2-4h2V8H9zm4 0h2V8h-2z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
<div class="dialog-actions">
|
<p v-if="typeof message === 'string'">{{ message }}</p>
|
||||||
|
<component :is="message" v-else />
|
||||||
|
|
||||||
|
<div class="actions">
|
||||||
<button @click="confirm">Confirm</button>
|
<button @click="confirm">Confirm</button>
|
||||||
<button @click="cancel">Cancel</button>
|
<button @click="cancel">Cancel</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -42,16 +50,34 @@ function cancel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.dialog {
|
.dialog {
|
||||||
background: white;
|
background: #2d3748;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dialog-actions {
|
.dialog .actions {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dialog .actions button {
|
||||||
|
background: #4a5568;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 8px 16px;
|
||||||
|
border-radius: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog .actions button:first-of-type:hover {
|
||||||
|
background: #2563eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog .actions button:last-of-type:hover {
|
||||||
|
background: #e53e3e;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In a real world example you would want to avoid creating refs in a global scope like this
|
||||||
|
*/
|
||||||
const isVisible = ref(false)
|
const isVisible = ref(false)
|
||||||
const message = ref('')
|
const message = ref('')
|
||||||
let resolveCallback
|
let resolveCallback
|
||||||
|
|||||||
@@ -6,17 +6,17 @@ import ConnectionLine from './SnappableConnectionLine.vue'
|
|||||||
const nodes = ref([
|
const nodes = ref([
|
||||||
{
|
{
|
||||||
id: '1',
|
id: '1',
|
||||||
label: 'Node 1',
|
data: { label: 'Node 1' },
|
||||||
position: { x: 0, y: 0 },
|
position: { x: 0, y: 0 },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '2',
|
id: '2',
|
||||||
label: 'Node 2',
|
data: { label: 'Node 2' },
|
||||||
position: { x: 100, y: 100 },
|
position: { x: 100, y: 100 },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '3',
|
id: '3',
|
||||||
label: 'Node 3',
|
data: { label: 'Node 3' },
|
||||||
position: { x: 200, y: 0 },
|
position: { x: 200, y: 0 },
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ const nodes = ref([
|
|||||||
{
|
{
|
||||||
id: '1',
|
id: '1',
|
||||||
type: 'input',
|
type: 'input',
|
||||||
label: 'Node 1',
|
data: { label: 'Node 1' },
|
||||||
position: { x: 250, y: 5 },
|
position: { x: 250, y: 5 },
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -21,8 +21,6 @@ const nodes = ref([
|
|||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
const colorSelectorData = toRef(() => nodes.value[0].data)
|
|
||||||
|
|
||||||
const edges = ref([
|
const edges = ref([
|
||||||
{
|
{
|
||||||
id: 'e1a-2',
|
id: 'e1a-2',
|
||||||
@@ -30,19 +28,21 @@ const edges = ref([
|
|||||||
sourceHandle: 'a',
|
sourceHandle: 'a',
|
||||||
target: '2',
|
target: '2',
|
||||||
animated: true,
|
animated: true,
|
||||||
style: () => ({
|
style: {
|
||||||
stroke: colorSelectorData.value?.color,
|
stroke: presets.ayame,
|
||||||
}),
|
},
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
|
const colorSelectorData = toRef(() => nodes.value[0].data)
|
||||||
|
|
||||||
// minimap stroke color functions
|
// minimap stroke color functions
|
||||||
function nodeStroke(n) {
|
function nodeStroke(n) {
|
||||||
switch (n.type) {
|
switch (n.type) {
|
||||||
case 'input':
|
case 'input':
|
||||||
return '#0041d0'
|
return '#0041d0'
|
||||||
case 'color-selector':
|
case 'color-selector':
|
||||||
return presets.sumi
|
return n.data.color
|
||||||
case 'output':
|
case 'output':
|
||||||
return '#ff0072'
|
return '#ff0072'
|
||||||
default:
|
default:
|
||||||
@@ -52,7 +52,7 @@ function nodeStroke(n) {
|
|||||||
|
|
||||||
function nodeColor(n) {
|
function nodeColor(n) {
|
||||||
if (n.type === 'color-selector') {
|
if (n.type === 'color-selector') {
|
||||||
return colorSelectorData.value?.color
|
return n.data.color
|
||||||
}
|
}
|
||||||
|
|
||||||
return '#fff'
|
return '#fff'
|
||||||
@@ -66,11 +66,10 @@ function nodeColor(n) {
|
|||||||
class="custom-node-flow"
|
class="custom-node-flow"
|
||||||
:class="[colorSelectorData?.isGradient ? 'animated-bg-gradient' : '']"
|
:class="[colorSelectorData?.isGradient ? 'animated-bg-gradient' : '']"
|
||||||
:style="{ backgroundColor: colorSelectorData?.color }"
|
:style="{ backgroundColor: colorSelectorData?.color }"
|
||||||
:default-viewport="{ zoom: 1.5 }"
|
|
||||||
fit-view-on-init
|
fit-view-on-init
|
||||||
>
|
>
|
||||||
<template #node-color-selector="{ id }">
|
<template #node-color-selector="props">
|
||||||
<ColorSelectorNode :id="id" />
|
<ColorSelectorNode :id="props.id" :data="props.data" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #node-output>
|
<template #node-output>
|
||||||
|
|||||||
@@ -7,12 +7,23 @@ const props = defineProps({
|
|||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const { updateNodeData } = useVueFlow()
|
const { updateNodeData, getConnectedEdges } = useVueFlow()
|
||||||
|
|
||||||
function onSelect(color) {
|
function onSelect(color) {
|
||||||
updateNodeData(props.id, { color, isGradient: false })
|
updateNodeData(props.id, { color, isGradient: false })
|
||||||
|
|
||||||
|
const connectedEdges = getConnectedEdges(props.id)
|
||||||
|
for (const edge of connectedEdges) {
|
||||||
|
edge.style = {
|
||||||
|
stroke: color,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onGradient() {
|
function onGradient() {
|
||||||
@@ -28,6 +39,7 @@ function onGradient() {
|
|||||||
v-for="{ name: colorName, value: color } of colors"
|
v-for="{ name: colorName, value: color } of colors"
|
||||||
:key="colorName"
|
:key="colorName"
|
||||||
:title="colorName"
|
:title="colorName"
|
||||||
|
:class="{ selected: color === data.color }"
|
||||||
:style="{ backgroundColor: color }"
|
:style="{ backgroundColor: color }"
|
||||||
type="button"
|
type="button"
|
||||||
@click="onSelect(color)"
|
@click="onSelect(color)"
|
||||||
|
|||||||
@@ -9,6 +9,10 @@ const nodesData = useNodesData(() => connections.value[0]?.source)
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Handle type="target" :position="Position.Left" :style="{ backgroundColor: nodesData.data?.color, filter: 'invert(100%)' }" />
|
<Handle
|
||||||
|
type="target"
|
||||||
|
:position="Position.Left"
|
||||||
|
:style="{ height: '16px', width: '6px', backgroundColor: nodesData.data?.color, filter: 'invert(100%)' }"
|
||||||
|
/>
|
||||||
{{ nodesData.data?.isGradient ? 'GRADIENT' : nodesData.data?.color }}
|
{{ nodesData.data?.isGradient ? 'GRADIENT' : nodesData.data?.color }}
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
.custom-node-flow .vue-flow__edges {
|
.vue-flow__edges {
|
||||||
filter: invert(100%);
|
filter: invert(100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-node-flow .vue-flow__node-color-selector {
|
.vue-flow__handle {
|
||||||
|
height: 24px;
|
||||||
|
width: 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-flow__node-color-selector {
|
||||||
border: 1px solid #777;
|
border: 1px solid #777;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
@@ -15,7 +21,7 @@
|
|||||||
max-width: 250px;
|
max-width: 250px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-node-flow .vue-flow__node-color-selector .color-selector {
|
.vue-flow__node-color-selector .color-selector {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
@@ -25,20 +31,30 @@
|
|||||||
gap: 4px
|
gap: 4px
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-node-flow .vue-flow__node-color-selector .color-selector button {
|
.vue-flow__node-color-selector .color-selector button {
|
||||||
border: 1px solid #777;
|
border: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
width: 25px;
|
width: 25px;
|
||||||
height: 25px;
|
height: 25px;
|
||||||
border-radius: 25px;
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-node-flow .vue-flow__node-color-selector .color-selector button:hover {
|
.vue-flow__node-color-selector .color-selector button:hover {
|
||||||
-webkit-box-shadow: 0px 5px 10px 0px rgba(0, 0, 0, 0.5);
|
box-shadow: 0 0 0 2px #2563eb;
|
||||||
box-shadow: 0px 5px 10px 0px rgba(0, 0, 0, 0.5);
|
transition: box-shadow 0.2s;
|
||||||
transform: scale(105%);
|
}
|
||||||
transition: 250ms all ease;
|
|
||||||
|
.vue-flow__node-color-selector .color-selector button.selected {
|
||||||
|
box-shadow: 0 0 0 2px #2563eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-flow__node-color-selector .vue-flow__handle {
|
||||||
|
background-color: #ec4899;
|
||||||
|
height: 24px;
|
||||||
|
width: 8px;
|
||||||
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.animated-bg-gradient {
|
.animated-bg-gradient {
|
||||||
|
|||||||
@@ -15,14 +15,16 @@ onConnect(addEdges)
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="dndflow" @drop="onDrop">
|
<div class="dnd-flow" @drop="onDrop">
|
||||||
<VueFlow :nodes="nodes" @dragover="onDragOver" @dragleave="onDragLeave">
|
<VueFlow :nodes="nodes" @dragover="onDragOver" @dragleave="onDragLeave">
|
||||||
<DropzoneBackground
|
<DropzoneBackground
|
||||||
:style="{
|
:style="{
|
||||||
backgroundColor: isDragOver ? '#e7f3ff' : 'transparent',
|
backgroundColor: isDragOver ? '#e7f3ff' : 'transparent',
|
||||||
transition: 'background-color 0.2s ease',
|
transition: 'background-color 0.2s ease',
|
||||||
}"
|
}"
|
||||||
/>
|
>
|
||||||
|
<p v-if="isDragOver">Drop here</p>
|
||||||
|
</DropzoneBackground>
|
||||||
</VueFlow>
|
</VueFlow>
|
||||||
|
|
||||||
<Sidebar />
|
<Sidebar />
|
||||||
|
|||||||
@@ -3,9 +3,11 @@ import { Background } from '@vue-flow/background'
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div style="height: 100%; width: 100%">
|
<div class="dropzone-background">
|
||||||
<Background :size="2" :gap="20" pattern-color="#BDBDBD">
|
<Background :size="2" :gap="20" pattern-color="#BDBDBD" />
|
||||||
|
|
||||||
|
<div class="overlay">
|
||||||
<slot />
|
<slot />
|
||||||
</Background>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
+31
-12
@@ -1,51 +1,70 @@
|
|||||||
.dndflow {
|
.dnd-flow {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
display: flex;
|
display: flex;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dndflow aside {
|
.dnd-flow aside {
|
||||||
color: white;
|
color: white;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
border-right: 1px solid #eee;
|
border-right: 1px solid #eee;
|
||||||
padding: 15px 10px;
|
padding: 15px 10px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
background: rgba(16, 185, 129, 0.75);
|
background: rgba(16, 185, 129, 0.75);
|
||||||
-webkit-box-shadow: 0px 5px 10px 0px rgba(0,0,0,0.3);
|
-webkit-box-shadow: 0px 5px 10px 0px rgba(0, 0, 0, 0.3);
|
||||||
box-shadow: 0px 5px 10px 0px rgba(0,0,0,0.3);
|
box-shadow: 0px 5px 10px 0px rgba(0, 0, 0, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dndflow aside .nodes > * {
|
.dnd-flow aside .nodes > * {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
cursor: grab;
|
cursor: grab;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
-webkit-box-shadow: 5px 5px 10px 2px rgba(0,0,0,0.25);
|
-webkit-box-shadow: 5px 5px 10px 2px rgba(0, 0, 0, 0.25);
|
||||||
box-shadow: 5px 5px 10px 2px rgba(0,0,0,0.25);
|
box-shadow: 5px 5px 10px 2px rgba(0, 0, 0, 0.25);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dndflow aside .description {
|
.dnd-flow aside .description {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dndflow .vue-flow-wrapper {
|
.dnd-flow .vue-flow-wrapper {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 640px) {
|
@media screen and (min-width: 640px) {
|
||||||
.dndflow {
|
.dnd-flow {
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dndflow aside {
|
.dnd-flow aside {
|
||||||
min-width: 25%;
|
min-width: 25%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 639px) {
|
@media screen and (max-width: 639px) {
|
||||||
.dndflow aside .nodes {
|
.dnd-flow aside .nodes {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
gap: 5px;
|
gap: 5px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dropzone-background {
|
||||||
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropzone-background .overlay {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 1;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ export default function useDragAndDrop() {
|
|||||||
id: nodeId,
|
id: nodeId,
|
||||||
type: draggedType.value,
|
type: draggedType.value,
|
||||||
position,
|
position,
|
||||||
label: `[${nodeId}]`,
|
data: { label: nodeId },
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -4,10 +4,27 @@ import { Background } from '@vue-flow/background'
|
|||||||
import { Panel, VueFlow } from '@vue-flow/core'
|
import { Panel, VueFlow } from '@vue-flow/core'
|
||||||
|
|
||||||
const nodes = ref([
|
const nodes = ref([
|
||||||
{ id: '1', type: 'input', label: 'Node 1', position: { x: 250, y: 5 } },
|
{
|
||||||
{ id: '2', label: 'Node 2', position: { x: 100, y: 100 } },
|
id: '1',
|
||||||
{ id: '3', label: 'Node 3', position: { x: 400, y: 100 } },
|
type: 'input',
|
||||||
{ id: '4', label: 'Node 4', position: { x: 400, y: 200 } },
|
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 },
|
||||||
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
const edges = ref([
|
const edges = ref([
|
||||||
@@ -30,7 +47,19 @@ watch(isHidden, () => {
|
|||||||
|
|
||||||
<Panel position="top-right">
|
<Panel position="top-right">
|
||||||
<button type="button" @click="isHidden = !isHidden">
|
<button type="button" @click="isHidden = !isHidden">
|
||||||
{{ isHidden ? 'Show' : 'Hide' }}
|
<svg v-if="isHidden" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24">
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M12 9a3 3 0 0 0-3 3a3 3 0 0 0 3 3a3 3 0 0 0 3-3a3 3 0 0 0-3-3m0 8a5 5 0 0 1-5-5a5 5 0 0 1 5-5a5 5 0 0 1 5 5a5 5 0 0 1-5 5m0-12.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<svg v-else xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24">
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M11.83 9L15 12.16V12a3 3 0 0 0-3-3zm-4.3.8l1.55 1.55c-.05.21-.08.42-.08.65a3 3 0 0 0 3 3c.22 0 .44-.03.65-.08l1.55 1.55c-.67.33-1.41.53-2.2.53a5 5 0 0 1-5-5c0-.79.2-1.53.53-2.2M2 4.27l2.28 2.28l.45.45C3.08 8.3 1.78 10 1 12c1.73 4.39 6 7.5 11 7.5c1.55 0 3.03-.3 4.38-.84l.43.42L19.73 22L21 20.73L3.27 3M12 7a5 5 0 0 1 5 5c0 .64-.13 1.26-.36 1.82l2.93 2.93c1.5-1.25 2.7-2.89 3.43-4.75c-1.73-4.39-6-7.5-11-7.5c-1.4 0-2.74.25-4 .7l2.17 2.15C10.74 7.13 11.35 7 12 7"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</Panel>
|
</Panel>
|
||||||
</VueFlow>
|
</VueFlow>
|
||||||
|
|||||||
@@ -4,20 +4,38 @@ import { VueFlow } from '@vue-flow/core'
|
|||||||
import InteractionControls from './InteractionControls.vue'
|
import InteractionControls from './InteractionControls.vue'
|
||||||
|
|
||||||
const nodes = ref([
|
const nodes = ref([
|
||||||
{ id: '1', type: 'input', label: 'Node 1', position: { x: 250, y: 5 } },
|
{
|
||||||
{ id: '2', label: 'Node 2', position: { x: 100, y: 100 } },
|
id: '1',
|
||||||
{ id: '3', label: 'Node 3', position: { x: 400, y: 100 } },
|
type: 'input',
|
||||||
{ id: '4', label: 'Node 4', position: { x: 400, y: 200 } },
|
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 },
|
||||||
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
const edges = ref([
|
const edges = ref([
|
||||||
{ id: 'e1-2', source: '1', target: '2', animated: true },
|
{ id: 'e1-2', source: '1', target: '2' },
|
||||||
{ id: 'e1-3', source: '1', target: '3' },
|
{ id: 'e1-3', source: '1', target: '3' },
|
||||||
|
{ id: 'e3-4', source: '3', target: '4' },
|
||||||
])
|
])
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<VueFlow :nodes="nodes" :edges="edges" class="interactionflow" fit-view-on-init>
|
<VueFlow :nodes="nodes" :edges="edges" class="interaction-flow" fit-view-on-init>
|
||||||
<InteractionControls />
|
<InteractionControls />
|
||||||
</VueFlow>
|
</VueFlow>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -6,21 +6,41 @@ import { VueFlow, useVueFlow } from '@vue-flow/core'
|
|||||||
* You can either use `getIntersectingNodes` to check if a given node intersects with others
|
* You can either use `getIntersectingNodes` to check if a given node intersects with others
|
||||||
* or `isNodeIntersecting` to check if a node is intersecting with a given area
|
* or `isNodeIntersecting` to check if a node is intersecting with a given area
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
const { onNodeDrag, getIntersectingNodes, isNodeIntersecting: __, updateNode } = useVueFlow()
|
||||||
const { onNodeDrag, getIntersectingNodes, isNodeIntersecting, getNodes, updateNode } = useVueFlow()
|
|
||||||
|
|
||||||
const nodes = ref([
|
const nodes = ref([
|
||||||
{ id: '1', label: 'Node 1', position: { x: 0, y: 0 } },
|
{
|
||||||
{ id: '2', label: 'Node 2', position: { x: 400, y: 400 } },
|
id: '1',
|
||||||
{ id: '3', label: 'Node 3', position: { x: 400, y: 0 } },
|
data: { label: 'Node 1' },
|
||||||
{ id: '4', label: 'Node 4', position: { x: 0, y: 400 } },
|
position: { x: 0, y: 0 },
|
||||||
{ id: '5', style: { borderColor: 'red' }, label: 'Drag me over another node', position: { x: 200, y: 200 } },
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
data: { label: 'Node 2' },
|
||||||
|
position: { x: 400, y: 400 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '3',
|
||||||
|
data: { label: 'Node 3' },
|
||||||
|
position: { x: 400, y: 0 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '4',
|
||||||
|
data: { label: 'Node 4' },
|
||||||
|
position: { x: 0, y: 400 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '5',
|
||||||
|
style: { borderColor: 'red' },
|
||||||
|
data: { label: 'Drag me over another node' },
|
||||||
|
position: { x: 200, y: 200 },
|
||||||
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
onNodeDrag(({ intersections }) => {
|
onNodeDrag(({ node }) => {
|
||||||
const intersectionIds = intersections.map((intersection) => intersection.id)
|
const intersectionIds = getIntersectingNodes(node).map((intersection) => intersection.id)
|
||||||
|
|
||||||
for (const node of getNodes.value) {
|
for (const node of nodes.value) {
|
||||||
const isIntersecting = intersectionIds.includes(node.id)
|
const isIntersecting = intersectionIds.includes(node.id)
|
||||||
|
|
||||||
updateNode(node.id, { class: isIntersecting ? 'intersecting' : '' })
|
updateNode(node.id, { class: isIntersecting ? 'intersecting' : '' })
|
||||||
|
|||||||
@@ -4,10 +4,31 @@ import { Background } from '@vue-flow/background'
|
|||||||
import { Panel, VueFlow } from '@vue-flow/core'
|
import { Panel, VueFlow } from '@vue-flow/core'
|
||||||
|
|
||||||
const nodes = ref([
|
const nodes = ref([
|
||||||
{ id: '1', type: 'input', label: 'Node 1', position: { x: 250, y: 5 }, class: 'light' },
|
{
|
||||||
{ id: '2', label: 'Node 2', position: { x: 100, y: 100 }, class: 'light' },
|
id: '1',
|
||||||
{ id: '3', label: 'Node 3', position: { x: 400, y: 100 }, class: 'light' },
|
type: 'input',
|
||||||
{ id: '4', label: 'Node 4', position: { x: 400, y: 200 }, class: 'light' },
|
data: { label: 'Node 1' },
|
||||||
|
position: { x: 250, y: 5 },
|
||||||
|
class: 'light',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
data: { label: 'Node 2' },
|
||||||
|
position: { x: 100, y: 100 },
|
||||||
|
class: 'light',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '3',
|
||||||
|
data: { label: 'Node 3' },
|
||||||
|
position: { x: 400, y: 100 },
|
||||||
|
class: 'light',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '4',
|
||||||
|
data: { label: 'Node 4' },
|
||||||
|
position: { x: 400, y: 200 },
|
||||||
|
class: 'light',
|
||||||
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
const edges = ref([
|
const edges = ref([
|
||||||
|
|||||||
@@ -6,56 +6,66 @@ import { Controls } from '@vue-flow/controls'
|
|||||||
import { MiniMap } from '@vue-flow/minimap'
|
import { MiniMap } from '@vue-flow/minimap'
|
||||||
|
|
||||||
const nodes = ref([
|
const nodes = ref([
|
||||||
{ id: '1', type: 'input', label: 'node', position: { x: 250, y: 0 } },
|
{
|
||||||
|
id: '1',
|
||||||
|
type: 'input',
|
||||||
|
data: { label: 'node' },
|
||||||
|
position: { x: 250, y: 0 },
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: '2',
|
id: '2',
|
||||||
label: 'parent node',
|
data: { label: 'parent node' },
|
||||||
position: { x: 100, y: 100 },
|
position: { x: 100, y: 100 },
|
||||||
style: { backgroundColor: 'rgba(16, 185, 129, 0.5)', width: '200px', height: '200px' },
|
style: { backgroundColor: 'rgba(16, 185, 129, 0.5)', width: '200px', height: '200px' },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '2a',
|
id: '2a',
|
||||||
label: 'child node',
|
data: { label: 'child node' },
|
||||||
position: { x: 10, y: 50 },
|
position: { x: 10, y: 50 },
|
||||||
parentNode: '2',
|
parentNode: '2',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '4',
|
id: '4',
|
||||||
label: 'parent node',
|
data: { label: 'parent node' },
|
||||||
position: { x: 320, y: 175 },
|
position: { x: 320, y: 175 },
|
||||||
style: { backgroundColor: 'rgba(16, 185, 129, 0.5)', width: '400px', height: '300px' },
|
style: { backgroundColor: 'rgba(16, 185, 129, 0.5)', width: '400px', height: '300px' },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '4a',
|
id: '4a',
|
||||||
label: 'child node',
|
data: { label: 'child node' },
|
||||||
position: { x: 15, y: 65 },
|
position: { x: 15, y: 65 },
|
||||||
extent: 'parent',
|
extent: 'parent',
|
||||||
parentNode: '4',
|
parentNode: '4',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '4b',
|
id: '4b',
|
||||||
label: 'nested parent node',
|
data: { label: 'nested parent node' },
|
||||||
position: { x: 15, y: 120 },
|
position: { x: 15, y: 120 },
|
||||||
style: { backgroundColor: 'rgba(139, 92, 246, 0.5)', height: '150px', width: '270px' },
|
style: { backgroundColor: 'rgba(139, 92, 246, 0.5)', height: '150px', width: '270px' },
|
||||||
parentNode: '4',
|
parentNode: '4',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '4b1',
|
id: '4b1',
|
||||||
label: 'nested child node',
|
data: { label: 'nested child node' },
|
||||||
position: { x: 20, y: 40 },
|
position: { x: 20, y: 40 },
|
||||||
parentNode: '4b',
|
parentNode: '4b',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '4b2',
|
id: '4b2',
|
||||||
label: 'nested child node',
|
data: { label: 'nested child node' },
|
||||||
position: { x: 100, y: 100 },
|
position: { x: 100, y: 100 },
|
||||||
parentNode: '4b',
|
parentNode: '4b',
|
||||||
},
|
},
|
||||||
{ id: '4c', label: 'child node', position: { x: 200, y: 65 }, parentNode: '4' },
|
{
|
||||||
|
id: '4c',
|
||||||
|
data: { label: 'child node' },
|
||||||
|
position: { x: 200, y: 65 },
|
||||||
|
parentNode: '4',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: '999',
|
id: '999',
|
||||||
type: 'input',
|
type: 'input',
|
||||||
label: 'Drag me to extend area!',
|
data: { label: 'Drag me to extend area!' },
|
||||||
position: { x: 20, y: 100 },
|
position: { x: 20, y: 100 },
|
||||||
class: 'light',
|
class: 'light',
|
||||||
expandParent: true,
|
expandParent: true,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ const nodes = ref([
|
|||||||
{
|
{
|
||||||
id: '1',
|
id: '1',
|
||||||
type: 'resizable',
|
type: 'resizable',
|
||||||
label: 'NodeResizer',
|
data: { label: 'NodeResizer' },
|
||||||
position: { x: 0, y: 0 },
|
position: { x: 0, y: 0 },
|
||||||
style: { background: '#fff', border: '2px solid black' },
|
style: { background: '#fff', border: '2px solid black' },
|
||||||
},
|
},
|
||||||
@@ -17,7 +17,7 @@ const nodes = ref([
|
|||||||
<template>
|
<template>
|
||||||
<VueFlow :nodes="nodes" fit-view-on-init>
|
<VueFlow :nodes="nodes" fit-view-on-init>
|
||||||
<template #node-resizable="resizableNodeProps">
|
<template #node-resizable="resizableNodeProps">
|
||||||
<ResizableNode :label="resizableNodeProps.label" />
|
<ResizableNode :data="resizableNodeProps.data" />
|
||||||
</template>
|
</template>
|
||||||
</VueFlow>
|
</VueFlow>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -2,13 +2,13 @@
|
|||||||
import { Handle, Position } from '@vue-flow/core'
|
import { Handle, Position } from '@vue-flow/core'
|
||||||
import { NodeResizer } from '@vue-flow/node-resizer'
|
import { NodeResizer } from '@vue-flow/node-resizer'
|
||||||
|
|
||||||
defineProps(['label'])
|
defineProps(['data'])
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NodeResizer min-width="100" min-height="30" />
|
<NodeResizer min-width="100" min-height="30" />
|
||||||
|
|
||||||
<Handle type="target" :position="Position.Left" />
|
<Handle type="target" :position="Position.Left" />
|
||||||
<div style="padding: 10px">{{ label }}</div>
|
<div style="padding: 10px">{{ data.label }}</div>
|
||||||
<Handle type="source" :position="Position.Right" />
|
<Handle type="source" :position="Position.Right" />
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -3,61 +3,83 @@ import { ref } from 'vue'
|
|||||||
import { Position, VueFlow } from '@vue-flow/core'
|
import { Position, VueFlow } from '@vue-flow/core'
|
||||||
import ToolbarNode from './ToolbarNode.vue'
|
import ToolbarNode from './ToolbarNode.vue'
|
||||||
|
|
||||||
const defaultNodeStyle = {
|
|
||||||
border: '1px solid #10b981',
|
|
||||||
background: '#ef467e',
|
|
||||||
color: 'white',
|
|
||||||
borderRadius: '99px',
|
|
||||||
}
|
|
||||||
|
|
||||||
const nodes = ref([
|
const nodes = ref([
|
||||||
{
|
{
|
||||||
id: '1',
|
id: '1',
|
||||||
type: 'toolbar',
|
type: 'menu',
|
||||||
label: 'toolbar top',
|
data: { label: 'toolbar top', toolbarPosition: Position.Top },
|
||||||
data: { toolbarPosition: Position.Top },
|
|
||||||
position: { x: 200, y: 0 },
|
position: { x: 200, y: 0 },
|
||||||
style: defaultNodeStyle,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '2',
|
id: '2',
|
||||||
type: 'toolbar',
|
type: 'menu',
|
||||||
label: 'toolbar right',
|
data: { label: 'toolbar right', toolbarPosition: Position.Right },
|
||||||
data: { toolbarPosition: Position.Right },
|
|
||||||
position: { x: -50, y: 100 },
|
position: { x: -50, y: 100 },
|
||||||
style: defaultNodeStyle,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '3',
|
id: '3',
|
||||||
type: 'toolbar',
|
type: 'menu',
|
||||||
label: 'toolbar bottom',
|
data: { label: 'toolbar bottom', toolbarPosition: Position.Bottom },
|
||||||
data: { toolbarPosition: Position.Bottom },
|
|
||||||
position: { x: 0, y: 200 },
|
position: { x: 0, y: 200 },
|
||||||
style: defaultNodeStyle,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '4',
|
id: '4',
|
||||||
type: 'toolbar',
|
type: 'menu',
|
||||||
label: 'toolbar left',
|
data: { label: 'toolbar left', toolbarPosition: Position.Left },
|
||||||
data: { toolbarPosition: Position.Left },
|
|
||||||
position: { x: 200, y: 300 },
|
position: { x: 200, y: 300 },
|
||||||
style: defaultNodeStyle,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '5',
|
id: '5',
|
||||||
type: 'toolbar',
|
type: 'menu',
|
||||||
label: 'toolbar always open',
|
data: { label: 'toolbar always open', toolbarPosition: Position.Top, toolbarVisible: true },
|
||||||
data: { toolbarPosition: Position.Top, toolbarVisible: true },
|
|
||||||
position: { x: 0, y: -100 },
|
position: { x: 0, y: -100 },
|
||||||
style: defaultNodeStyle,
|
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<VueFlow :nodes="nodes" fit-view-on-init class="vue-flow-basic-example">
|
<VueFlow :nodes="nodes" fit-view-on-init>
|
||||||
<template #node-toolbar="nodeProps">
|
<template #node-menu="props">
|
||||||
<ToolbarNode :data="nodeProps.data" :label="nodeProps.label" />
|
<ToolbarNode :id="props.id" :data="props.data" />
|
||||||
</template>
|
</template>
|
||||||
</VueFlow>
|
</VueFlow>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.vue-flow__node-toolbar {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
align-items: center;
|
||||||
|
background-color: #2d3748;
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-flow__node-toolbar button {
|
||||||
|
background: #4a5568;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-flow__node-toolbar button.selected {
|
||||||
|
background: #2563eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-flow__node-toolbar button:hover {
|
||||||
|
background: #2563eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-flow__node-menu {
|
||||||
|
padding: 16px 24px;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-flow__node-menu.selected {
|
||||||
|
box-shadow: 0 0 0 2px #2563eb;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1,23 +1,29 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { Handle, Position } from '@vue-flow/core'
|
import { Handle, Position, useVueFlow } from '@vue-flow/core'
|
||||||
import { NodeToolbar } from '@vue-flow/node-toolbar'
|
import { NodeToolbar } from '@vue-flow/node-toolbar'
|
||||||
|
|
||||||
defineProps(['data', 'label'])
|
const props = defineProps(['id', 'data'])
|
||||||
|
|
||||||
|
const actions = ['👎', '✋', '👍']
|
||||||
|
|
||||||
|
const { updateNodeData } = useVueFlow()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NodeToolbar
|
<NodeToolbar :is-visible="data.toolbarVisible" :position="data.toolbarPosition">
|
||||||
style="display: flex; gap: 0.5rem; align-items: center"
|
<button
|
||||||
:is-visible="data.toolbarVisible"
|
v-for="action of actions"
|
||||||
:position="data.toolbarPosition"
|
:key="action"
|
||||||
>
|
type="button"
|
||||||
<button>Action1</button>
|
:class="{ selected: action === data.action }"
|
||||||
<button>Action2</button>
|
@click="updateNodeData(props.id, { action })"
|
||||||
<button>Action3</button>
|
>
|
||||||
|
{{ action }}
|
||||||
|
</button>
|
||||||
</NodeToolbar>
|
</NodeToolbar>
|
||||||
|
|
||||||
<div :style="{ padding: '10px 20px' }">
|
<div>
|
||||||
{{ label }}
|
{{ data.label }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Handle type="target" :position="Position.Left" />
|
<Handle type="target" :position="Position.Left" />
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { VueFlow } from '@vue-flow/core'
|
|||||||
import { Background } from '@vue-flow/background'
|
import { Background } from '@vue-flow/background'
|
||||||
import SaveRestoreControls from './Controls.vue'
|
import SaveRestoreControls from './Controls.vue'
|
||||||
|
|
||||||
const nodes = ref([{ id: '1', label: 'Node 1', position: { x: 100, y: 100 } }])
|
const nodes = ref([{ id: '1', data: { label: 'Node 1' }, position: { x: 100, y: 100 } }])
|
||||||
|
|
||||||
const edges = ref([])
|
const edges = ref([])
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export function getElements(xElements = 10, yElements = 10) {
|
|||||||
const node = {
|
const node = {
|
||||||
id: nodeId.toString(),
|
id: nodeId.toString(),
|
||||||
style: { width: `50px`, fontSize: `11px`, zIndex: 1 },
|
style: { width: `50px`, fontSize: `11px`, zIndex: 1 },
|
||||||
label: `Node ${nodeId}`,
|
data: { label: `Node ${nodeId}` },
|
||||||
class: 'light',
|
class: 'light',
|
||||||
position,
|
position,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,24 +7,21 @@ import TeleportableNode from './TeleportableNode.vue'
|
|||||||
const nodes = ref([
|
const nodes = ref([
|
||||||
{
|
{
|
||||||
id: '1',
|
id: '1',
|
||||||
label: 'Click to teleport',
|
|
||||||
type: 'teleportable',
|
type: 'teleportable',
|
||||||
position: { x: 125, y: 0 },
|
position: { x: 125, y: 0 },
|
||||||
data: {},
|
data: { label: 'Click to teleport' },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '2',
|
id: '2',
|
||||||
label: 'Click to teleport',
|
|
||||||
type: 'teleportable',
|
type: 'teleportable',
|
||||||
position: { x: 350, y: 200 },
|
position: { x: 350, y: 200 },
|
||||||
data: {},
|
data: { label: 'Click to teleport' },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '3',
|
id: '3',
|
||||||
label: 'Click to teleport',
|
|
||||||
type: 'teleportable',
|
type: 'teleportable',
|
||||||
position: { x: 0, y: 200 },
|
position: { x: 0, y: 200 },
|
||||||
data: {},
|
data: { label: 'Click to teleport' },
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|||||||
@@ -1,38 +1,47 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { Position, VueFlow, useVueFlow } from '@vue-flow/core'
|
import { Position, VueFlow, useVueFlow } from '@vue-flow/core'
|
||||||
|
import { Background } from '@vue-flow/background'
|
||||||
import TransitionEdge from './TransitionEdge.vue'
|
import TransitionEdge from './TransitionEdge.vue'
|
||||||
|
|
||||||
|
const { onInit } = useVueFlow()
|
||||||
|
|
||||||
const nodes = ref([
|
const nodes = ref([
|
||||||
{
|
{
|
||||||
id: '1',
|
id: '1',
|
||||||
type: 'input',
|
type: 'input',
|
||||||
label: 'Dblclick me',
|
data: { label: 'DblClick me' },
|
||||||
position: { x: 0, y: 0 },
|
position: { x: 0, y: 0 },
|
||||||
sourcePosition: Position.Right,
|
sourcePosition: Position.Right,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '2',
|
id: '2',
|
||||||
type: 'output',
|
type: 'output',
|
||||||
label: 'Dblclick me',
|
data: { label: 'DblClick me' },
|
||||||
position: { x: 1000, y: 1000 },
|
position: { x: 1000, y: 1000 },
|
||||||
targetPosition: Position.Left,
|
targetPosition: Position.Left,
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
const edges = ref([{ id: 'e1-2', type: 'custom', source: '1', target: '2', animated: true, style: { stroke: '#fff' } }])
|
const edges = ref([{ id: 'e1-2', type: 'custom', source: '1', target: '2', style: { stroke: '#fff' } }])
|
||||||
|
|
||||||
const { onPaneReady } = useVueFlow()
|
onInit(({ fitView }) => {
|
||||||
|
|
||||||
onPaneReady(({ fitView }) => {
|
|
||||||
fitView({ nodes: ['1'] })
|
fitView({ nodes: ['1'] })
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<VueFlow :nodes="nodes" :edges="edges" :style="{ backgroundColor: '#1A192B' }">
|
<VueFlow :nodes="nodes" :edges="edges" class="transition-flow">
|
||||||
|
<Background />
|
||||||
|
|
||||||
<template #edge-custom="props">
|
<template #edge-custom="props">
|
||||||
<TransitionEdge v-bind="props" />
|
<TransitionEdge v-bind="props" />
|
||||||
</template>
|
</template>
|
||||||
</VueFlow>
|
</VueFlow>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.transition-flow {
|
||||||
|
background-color: #1a192b;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -9,17 +9,17 @@ const nodes = ref([
|
|||||||
{
|
{
|
||||||
id: '1',
|
id: '1',
|
||||||
type: 'input',
|
type: 'input',
|
||||||
label: 'Node <strong>A</strong>',
|
data: { label: 'Node <strong>A</strong>' },
|
||||||
position: { x: 250, y: 0 },
|
position: { x: 250, y: 0 },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '2',
|
id: '2',
|
||||||
label: 'Node <strong>B</strong>',
|
data: { label: 'Node <strong>B</strong>' },
|
||||||
position: { x: 100, y: 100 },
|
position: { x: 100, y: 100 },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '3',
|
id: '3',
|
||||||
label: 'Node <strong>C</strong>',
|
data: { label: 'Node <strong>C</strong>' },
|
||||||
position: { x: 400, y: 100 },
|
position: { x: 400, y: 100 },
|
||||||
style: { background: '#D6D5E6', color: '#333', border: '1px solid #222138', width: 180 },
|
style: { background: '#D6D5E6', color: '#333', border: '1px solid #222138', width: 180 },
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,44 +1,42 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { reactive, ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { VueFlow, useVueFlow } from '@vue-flow/core'
|
import { Panel, VueFlow, useVueFlow } from '@vue-flow/core'
|
||||||
|
|
||||||
const { onPaneReady, findNode, updateNode } = useVueFlow()
|
const { updateNode } = useVueFlow()
|
||||||
|
|
||||||
const opts = reactive({
|
const bgColor = ref('#eeeeee')
|
||||||
bg: '#eeeeee',
|
|
||||||
label: 'Node 1',
|
const label = ref('Node 1')
|
||||||
hidden: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
const nodes = ref([
|
const nodes = ref([
|
||||||
{ id: '1', label: opts.label, style: { backgroundColor: opts.bg }, hidden: opts.hidden, position: { x: 100, y: 100 } },
|
{
|
||||||
{ id: '2', label: 'Node 2', position: { x: 100, y: 200 } },
|
id: '1',
|
||||||
|
data: { label: label.value },
|
||||||
|
style: { backgroundColor: bgColor.value },
|
||||||
|
position: { x: 100, y: 100 },
|
||||||
|
},
|
||||||
|
{ id: '2', data: { label: 'Node 2' }, position: { x: 100, y: 200 } },
|
||||||
])
|
])
|
||||||
|
|
||||||
const edges = ref([{ id: 'e1-2', source: '1', target: '2' }])
|
const edges = ref([{ id: 'e1-2', source: '1', target: '2' }])
|
||||||
|
|
||||||
onPaneReady(({ fitView }) => {
|
|
||||||
fitView()
|
|
||||||
})
|
|
||||||
|
|
||||||
function handleUpdate() {
|
function handleUpdate() {
|
||||||
updateNode(nodes.value[0].id, { label: opts.label, style: { backgroundColor: opts.bg }, hidden: opts.hidden })
|
updateNode('1', { data: { label: label.value }, style: { backgroundColor: bgColor.value } })
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<VueFlow :nodes="nodes" :edges="edges">
|
<VueFlow :nodes="nodes" :edges="edges" fit-view-on-init>
|
||||||
<div class="updatenode__controls">
|
<Panel position="top-right">
|
||||||
<label>label:</label>
|
<div class="field">
|
||||||
<input v-model="opts.label" @input="handleUpdate" />
|
<label for="label">Label:</label>
|
||||||
|
<input id="label" v-model="label" @input="handleUpdate" />
|
||||||
<label class="updatenode__bglabel">background:</label>
|
|
||||||
<input v-model="opts.bg" type="color" @input="handleUpdate" />
|
|
||||||
|
|
||||||
<div class="updatenode__checkboxwrapper">
|
|
||||||
<label>hidden:</label>
|
|
||||||
<input v-model="opts.hidden" type="checkbox" @change="handleUpdate" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
<div class="field">
|
||||||
|
<label for="bgColor">Background color:</label>
|
||||||
|
<input id="bgColor" v-model="bgColor" type="color" @input="handleUpdate" />
|
||||||
|
</div>
|
||||||
|
</Panel>
|
||||||
</VueFlow>
|
</VueFlow>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,30 +1,24 @@
|
|||||||
.updatenode__controls {
|
.vue-flow__panel {
|
||||||
position: absolute;
|
background-color: #2d3748;
|
||||||
right: 10px;
|
color: white;
|
||||||
top: 10px;
|
border-radius: 8px;
|
||||||
z-index: 4;
|
|
||||||
font-size: 11px;
|
|
||||||
background-color: lightgray;
|
|
||||||
border-radius: 10px;
|
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.updatenode__controls label {
|
.vue-flow__panel .field {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-flow__panel label {
|
||||||
display: blocK;
|
display: blocK;
|
||||||
}
|
}
|
||||||
|
|
||||||
.updatenode__controls input {
|
.vue-flow__panel input {
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.updatenode__bglabel {
|
|
||||||
margin-top: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.updatenode__checkboxwrapper {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
margin-top: 8px;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -7,15 +7,30 @@ import CustomNode from './CustomNode.vue'
|
|||||||
const { addEdges } = useVueFlow()
|
const { addEdges } = useVueFlow()
|
||||||
|
|
||||||
const nodes = ref([
|
const nodes = ref([
|
||||||
{ id: '0', type: 'custominput', position: { x: 0, y: 150 }, isValidTargetPos: (connection) => connection.target === 'B' },
|
{
|
||||||
|
id: '0',
|
||||||
|
type: 'custominput',
|
||||||
|
position: { x: 0, y: 150 },
|
||||||
|
isValidTargetPos: (connection) => connection.target === 'B',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'A',
|
id: 'A',
|
||||||
type: 'custom',
|
type: 'custom',
|
||||||
position: { x: 250, y: 0 },
|
position: { x: 250, y: 0 },
|
||||||
isValidSourcePos: () => false,
|
isValidSourcePos: () => false,
|
||||||
},
|
},
|
||||||
{ id: 'B', type: 'custom', position: { x: 250, y: 150 }, isValidSourcePos: (connection) => connection.target === 'B' },
|
{
|
||||||
{ id: 'C', type: 'custom', position: { x: 250, y: 300 }, isValidSourcePos: (connection) => connection.target === 'B' },
|
id: 'B',
|
||||||
|
type: 'custom',
|
||||||
|
position: { x: 250, y: 150 },
|
||||||
|
isValidSourcePos: (connection) => connection.target === 'B',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'C',
|
||||||
|
type: 'custom',
|
||||||
|
position: { x: 250, y: 300 },
|
||||||
|
isValidSourcePos: (connection) => connection.target === 'B',
|
||||||
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
const edges = ref([])
|
const edges = ref([])
|
||||||
|
|||||||
@@ -209,7 +209,7 @@ export default defineConfigWithTheme<DefaultTheme.Config>({
|
|||||||
{ text: 'Math Operation Flow', link: '/examples/math' },
|
{ text: 'Math Operation Flow', link: '/examples/math' },
|
||||||
{ text: 'Screenshot', link: '/examples/screenshot' },
|
{ text: 'Screenshot', link: '/examples/screenshot' },
|
||||||
{ text: 'Confirm Delete', link: '/examples/confirm' },
|
{ text: 'Confirm Delete', link: '/examples/confirm' },
|
||||||
{ text: 'Node Visibility', link: '/examples/hidden' },
|
{ text: 'Hidden', link: '/examples/hidden' },
|
||||||
{ text: 'Node Intersections', link: '/examples/intersection' },
|
{ text: 'Node Intersections', link: '/examples/intersection' },
|
||||||
{ text: 'Multiple Flows', link: '/examples/multi' },
|
{ text: 'Multiple Flows', link: '/examples/multi' },
|
||||||
{ text: 'Pinia Store', link: '/examples/pinia' },
|
{ text: 'Pinia Store', link: '/examples/pinia' },
|
||||||
|
|||||||
Reference in New Issue
Block a user