docs: add multiple transition animations to teleport example

# What's changed?

* add fade and shrink animations to teleportable nodes
* demonstrate using `updateNodeDimensions` to properly align edges after a resizing transition
This commit is contained in:
bcakmakoglu
2022-06-15 22:38:57 +02:00
parent 2de121509e
commit 597f799fa4
7 changed files with 167 additions and 56 deletions
+2 -1
View File
@@ -28,7 +28,8 @@ body,
.vue-flow__minimap {
transform: scale(75%);
transform-origin: bottom right;
}`
}
\n`
const store = new ReplStore({
showOutput: true,
+2 -1
View File
@@ -14,7 +14,7 @@ import { HiddenApp } from './hidden'
import { InteractionApp, InteractionCSS, InteractionControls } from './interaction'
import { MultiApp, MultiCSS, MultiFlow } from './multi'
import { HorizontalApp, HorizontalElements } from './horizontal'
import { TeleportApp, TeleportCSS, TeleportSidebar, TeleportableNode } from './teleport'
import { TeleportApp, TeleportCSS, TeleportSidebar, TeleportableNode, TeleportableUseTransition } from './teleport'
export const exampleImports = {
basic: {
@@ -94,6 +94,7 @@ export const exampleImports = {
'App.vue': TeleportApp,
'Sidebar.vue': TeleportSidebar,
'TeleportableNode.vue': TeleportableNode,
'useTransition.js': TeleportableUseTransition,
'style.css': TeleportCSS,
},
}
+2 -2
View File
@@ -1,5 +1,5 @@
<script setup>
import { VueFlow, useVueFlow } from '@braks/vue-flow'
import { VueFlow } from '@braks/vue-flow'
import { ref } from 'vue'
import Sidebar from './Sidebar.vue'
import TeleportableNode from './TeleportableNode.vue'
@@ -38,7 +38,7 @@ const elements = ref([
<div class="teleportflow">
<VueFlow v-model="elements" :fit-view-on-init="true">
<template #node-teleportable="props">
<TeleportableNode :id="props.id" />
<TeleportableNode v-bind="props" />
</template>
</VueFlow>
<Sidebar />
@@ -1,72 +1,40 @@
<script setup>
import { Handle, Position, getConnectedEdges, useVueFlow } from '@braks/vue-flow'
import { ref } from 'vue'
import { Handle, Position } from '@braks/vue-flow'
import { watch } from 'vue'
import { useTransition } from './useTransition.js'
const props = defineProps({
id: {
type: String,
required: true,
},
nodeElement: {
type: Object,
required: false,
},
})
const { edges, getNode } = useVueFlow()
const { animation, transition, teleport, nodeElement, onClick } = useTransition(props.id, props.nodeElement)
const teleport = ref(null)
const transition = ref(false)
watch(props, () => {
if (props.nodeElement) nodeElement.value = props.nodeElement
})
/**
* specify a selector to teleport to
*
* teleported elements still behave like they're at their position before,
* i.e. if they emit events, they will still emit them up their regular tree
*/
const onClick = (destination) => {
const node = getNode.value(props.id)
transition.value = true
// save current teleport destination to data of node
node.data.destination = destination
// hide connected edges when teleporting
const connectedEdges = getConnectedEdges([node], edges.value)
// if destination is not null, hide edges immediately
// check if nodes connected to edge are teleported and hide edge if one of them is
if (destination) {
connectedEdges.forEach(
(edge) => (edge.hidden = !!getNode.value(edge.source).data.destination || !!getNode.value(edge.target).data.destination),
)
}
setTimeout(() => {
// teleport to destination or disable teleport
teleport.value = destination
setTimeout(() => {
transition.value = false
// if destination is null, defer hiding edges until node is teleported back
if (!destination) {
connectedEdges.forEach(
(edge) =>
(edge.hidden = !!getNode.value(edge.source).data.destination || !!getNode.value(edge.target).data.destination),
)
}
}, 500)
}, 500)
const changeAnimation = () => {
animation.value = animation.value === 'fade' ? 'shrink' : 'fade'
}
</script>
<template>
<teleport :disabled="!teleport" :to="teleport">
<transition name="fade">
<transition :name="animation">
<div v-if="!transition" class="teleportable">
<Handle type="target" :position="Position.Top" />
[Node {{ id }}]
<div class="buttons">
<div v-if="teleport !== '#port'" class="button" @click.prevent="onClick('#port')">Teleport To Sidebar</div>
<div v-if="teleport !== null" class="button" @click.prevent="onClick(null)">Teleport To Main Graph</div>
<div class="button" @click.prevent="changeAnimation">Animation: {{ animation }}</div>
</div>
<Handle type="source" :position="Position.Bottom" />
</div>
@@ -1,4 +1,5 @@
export { default as TeleportApp } from './App.vue?raw'
export { default as TeleportSidebar } from './Sidebar.vue?raw'
export { default as TeleportableNode } from './TeleportableNode.vue?raw'
export { default as TeleportableUseTransition } from './useTransition.js?raw'
export { default as TeleportCSS } from './style.css'
+36 -5
View File
@@ -5,10 +5,13 @@
}
.teleportflow aside {
display: flex;
flex-direction: column;
gap: 5px;
color: white;
font-weight: 700;
border-right: 1px solid #eee;
padding: 15px 10px;
padding: 10px 10px;
font-size: 12px;
background: rgba(16, 185, 129, 0.75);
-webkit-box-shadow: 0px 5px 10px 0px rgba(0, 0, 0, 0.3);
@@ -59,16 +62,17 @@
color: black
}
.teleportable .buttons {
.buttons {
display: flex;
flex-direction: row;
flex-direction: column;
justify-content: center;
align-items: center;
margin-top: 5px;
gap: 5px;
}
.teleportable .buttons .button {
.button {
background-color: whitesmoke;
cursor: pointer;
padding: 5px 10px;
border: 1px solid black;
@@ -78,7 +82,7 @@
box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
}
.teleportable .buttons .button:hover {
.button:hover {
background: black;
color: white;
}
@@ -92,3 +96,30 @@
.fade-leave-to {
opacity: 0;
}
.shrink-leave-active {
animation: shrink 0.5s;
}
.shrink-enter-active {
animation: grow 0.5s;
}
@keyframes grow {
from {
transform: scale(0.1);
}
to {
transform: scale(1);
}
}
@keyframes shrink {
from {
transform: scale(1);
}
to {
transform: scale(0.1);
}
}
@@ -0,0 +1,109 @@
import { getConnectedEdges, useVueFlow } from '@braks/vue-flow'
import { nextTick, ref } from 'vue'
/**
* Utility composable for specifying animations
*
* Animations that resize a node need to call the `updateNodeDimensions` function from store to update node handle positions
* Otherwise edges do not connect properly
*/
export const useTransition = (id) => {
const nodeElement = ref()
const animation = ref('fade')
const transition = ref(false)
const teleport = ref(null)
const { updateNodeDimensions, getNode, edges } = useVueFlow()
/**
* specify a selector to teleport to
*
* teleported elements still behave like they're at their position before,
* i.e. if they emit events, they will still emit them up their regular tree
*/
const fade = (destination, onFinish) => {
setTimeout(() => {
// teleport to destination or disable teleport
teleport.value = destination
setTimeout(() => {
transition.value = false
// if destination is null, defer hiding edges until node is teleported back
if (!destination) {
onFinish()
}
}, 500)
}, 500)
}
const shrink = (destination, onFinish) => {
setTimeout(() => {
// teleport to destination or disable teleport
teleport.value = destination
setTimeout(() => {
transition.value = false
setTimeout(() => {
// if destination is null, defer hiding edges until node is teleported back
if (!destination) {
updateNodeDimensions([{ id, nodeElement: nodeElement.value, forceUpdate: true }])
nextTick(() => {
onFinish()
})
}
}, 500)
}, 500)
}, 500)
}
/**
* specify a selector to teleport to
*
* teleported elements still behave like they're at their position before,
* i.e. if they emit events, they will still emit them up their regular tree
*/
const onClick = (destination) => {
const node = getNode.value(id)
transition.value = true
// save current teleport destination to data of node
node.data.destination = destination
// hide connected edges when teleporting
const connectedEdges = getConnectedEdges([node], edges.value)
// if destination is not null, hide edges immediately
// check if nodes connected to edge are teleported and hide edge if one of them is
if (destination) {
connectedEdges.forEach(
(edge) => (edge.hidden = !!getNode.value(edge.source).data.destination || !!getNode.value(edge.target).data.destination),
)
}
const onFinish = () => {
connectedEdges.forEach(
(edge) => (edge.hidden = !!getNode.value(edge.source).data.destination || !!getNode.value(edge.target).data.destination),
)
}
switch (animation.value) {
case 'fade':
fade(destination, onFinish)
break
case 'shrink':
shrink(destination, onFinish)
}
}
return {
animation,
transition,
teleport,
nodeElement,
onClick,
}
}