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:
Braks
2024-06-10 23:51:47 +02:00
committed by braks
parent 5dc8711a6b
commit d71de0a92d
34 changed files with 528 additions and 244 deletions
+13 -10
View File
@@ -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>
+54 -9
View File
@@ -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',
},
] ]
+17 -13
View File
@@ -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;
} }
+22 -12
View File
@@ -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 />
+30 -4
View File
@@ -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
+3 -3
View File
@@ -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 },
}, },
]) ])
+1 -1
View File
@@ -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 },
}, },
]) ])
+9 -10
View File
@@ -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)"
+5 -1
View File
@@ -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>
+27 -11
View File
@@ -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 {
+4 -2
View File
@@ -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 />
+5 -3
View File
@@ -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
View File
@@ -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;
}
+1 -1
View File
@@ -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 },
} }
/** /**
+34 -5
View File
@@ -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>
+24 -6
View File
@@ -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>
+30 -10
View File
@@ -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' : '' })
+25 -4
View File
@@ -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([
+20 -10
View File
@@ -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,
+2 -2
View File
@@ -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 -2
View File
@@ -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>
+52 -30
View File
@@ -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>
+18 -12
View File
@@ -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" />
+1 -1
View File
@@ -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>
+1 -1
View File
@@ -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,
} }
+3 -6
View File
@@ -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' },
}, },
]) ])
+16 -7
View File
@@ -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>
+3 -3
View File
@@ -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 },
}, },
+25 -27
View File
@@ -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>
+15 -21
View File
@@ -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;
}
+18 -3
View File
@@ -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([])
+1 -1
View File
@@ -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' },