diff --git a/docs/components/examples/teleport/index.ts b/docs/components/examples/teleport/index.ts
index aeb04916..9a262f7b 100644
--- a/docs/components/examples/teleport/index.ts
+++ b/docs/components/examples/teleport/index.ts
@@ -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'
diff --git a/docs/components/examples/teleport/style.css b/docs/components/examples/teleport/style.css
index 473d4235..ef076e6f 100644
--- a/docs/components/examples/teleport/style.css
+++ b/docs/components/examples/teleport/style.css
@@ -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);
+ }
+}
+
diff --git a/docs/components/examples/teleport/useTransition.js b/docs/components/examples/teleport/useTransition.js
new file mode 100644
index 00000000..87856553
--- /dev/null
+++ b/docs/components/examples/teleport/useTransition.js
@@ -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,
+ }
+}