Run transition on programmatically created component - vue.js

I'm using this code
var ComponentClass = Vue.extend(Notification);
var instance = new ComponentClass({
propsData: {
notification: notification
}
});
instance.$mount();
this.$refs.notificationContainer.appendChild(instance.$el);
to programmatically create a component when a new notification is received through pusher, this is my notification component
<template>
<transition
enter-active-class="transition ease-out duration-100 transform"
enter-class="opacity-0 scale-95"
enter-to-class="opacity-100 scale-100"
leave-active-class="transition ease-in duration-75 transform"
leave-class="opacity-100 scale-100"
leave-to-class="opacity-0 scale-95"
>
<div class="bg-gray-100 px-4 py-2 border-0 rounded relative mb-4 text-gray-700 flex items-center">
<div class="flex-shrink-0 h-10 w-10">
<img
class="h-10 w-10 rounded-full"
:src="notification.user.profile_photo_url"
:alt="notification.user.username"
>
</div>
<span class="inline-block align-middle flex-1 ml-2 mr-4">
<strong>{{notification.user.username}}</strong> {{notification.message}}
</span>
<button class="focus:outline-none">
<i
class="fad fa-times text-red-600"
></i>
</button>
</div>
</transition>
</template>
<script>
export default {
props: {
notification: Object
}
}
</script>
but for some reason the transition doesnt run when the component is created

Fixed by adding appear to the transition element

Related

I'm trying to use Dialog headless ui as a way to open a modal out of my dropdown, but everything I've tried so far hasn't worked

Apologies as I'm new to headless ui and vue in general. I have a dropdown in my parent component that works. What I'm trying to do is use dialog to open a modal upon clicking the button in the dropdown. The issue is everyway I have thought to try either causes the dropdown to flash open and close, or the dropdown to open, allow me to click the modal button, but nothing happens, no modal appears. Below is my code for the dialog modal:
<template>
<div class="flex items-center justify-center">
<button type="button" #click="openModal" class="rounded-md bg-black bg-opacity-20 px-4 py-2 text-sm font-medium text-white hover:bg-opacity-30 focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75">
Open dialog
</button>
</div>
<TransitionRoot appear :show="isOpen" as="template">
<Dialog as="div" #close="closeModal" class="relative">
<TransitionChild as="template" enter="duration-300 ease-out" enter-from="opacity-0" enter-to="opacity-100" leave="duration-200 ease-in" leave-from="opacity-100" leave to="opacity-0">
<div class="fixed inset-0 bg-black bg-opacity-25" />
</TransitionChild>
<div class="fixed inset-0 overflow-y-auto">
<div
class="flex min-h-full items-center justify-center p-4 text-center"
>
<TransitionChild as="template" enter="duration-300 ease-out" enter-from="opacity-0 scale-95" enter-to="opacity-100 scale-100" leave="duration-200 ease-in" leave-from="opacity-100 scale-100" leave-to="opacity-0 scale-95">
<DialogPanel
class="w-full max-w-md transform overflow-hidden rounded-2xl bg-white p-6 text-left align-middle shadow-xl transition-all"
>
<DialogTitle
as="h3"
class="text-lg font-medium leading-6 text-gray-900"
>
Payment successful
</DialogTitle>
<div class="mt-2">
<p class="text-sm text-gray-500">
Your payment has been successfully submitted. We’ve sent you
an email with all of the details of your order.
</p>
</div>
<div class="mt-4">
<button
type="button"
class="inline-flex justify-center rounded-md border border-transparent bg-blue-100 px-4 py-2 text-sm font-medium text-blue-900 hover:bg-blue-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
#click="closeModal"
>
Got it, thanks!
</button>
</div>
</DialogPanel>
</TransitionChild>
</div>
</div>
</Dialog>
</TransitionRoot>
</template>
<script setup>
import { ref } from 'vue'
import {
TransitionRoot,
TransitionChild,
Dialog,
DialogPanel,
DialogTitle,
} from '#headlessui/vue'
const isOpen = ref(true)
function closeModal() {
isOpen.value = false
}
function openModal() {
isOpen.value = true
}
</script>

Fabric.js not rendering objects on top of canvas inside Vue 3 component

I tried adding a simple rectangle on top of a canvas using Fabric.js in Vue 3
I was able to load rectangle on top of canvas inside a page, where as it doesn't seem to work inside a component.
I tried putting a 2 second timeout as well in component, but it doesn't render anything.
Is there any workaround or am I making any mistake?
<template>
<TransitionRoot as="template" :show="showCanvas">
<Dialog as="div" class="fixed z-10 inset-0" #close="showCanvas = false">
<div class="w-full" style="width: 60%!important; height:auto">
<TransitionChild as="template" enter="ease-out duration-300" enter-from="opacity-0"
enter-to="opacity-100" leave="ease-in duration-200" leave-from="opacity-100" leave-to="opacity-0">
<DialogOverlay class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
</TransitionChild>
<!-- This element is to trick the browser into centering the modal contents. -->
<span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">​</span>
<TransitionChild as="template" enter="ease-out duration-300"
enter-from="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
enter-to="opacity-100 translate-y-0 sm:scale-100" leave="ease-in duration-200"
leave-from="opacity-100 translate-y-0 sm:scale-100"
leave-to="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95">
<div
class="bg-white">
<div>
<div class="mt-3 text-center sm:mt-5">
<!-- This example requires Tailwind CSS v2.0+ -->
<div class="fixed inset-0 overflow-y-auto z-50 flex justify-center items-center">
<div class=" bg-white m-auto rounded-lg shadow-md" role="dialog" aria-modal="true"
aria-labelledby="modal- headline" style="width: 550px !important;height:auto;">
<canvas id="canvas" ></canvas>
</div>
</div>
<div class="flex w-full mx-auto absolute bottom-56 justify-center z-50">
<button type="button"
class="w-50 mr-2 inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-indigo-600 text-base font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:col-start-2 sm:text-sm"
>
Save
</button>
<button type="button"
class="mt-3 w-50 ml-2 inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:col-start-1 sm:text-sm"
#click="closePopup" ref="cancelButtonRef">
Cancel
</button>
</div>
</div>
</div>
</div>
</TransitionChild>
</div>
</Dialog>
</TransitionRoot>
</template>
<script>
import { fabric } from 'fabric'
import { ref, watch, reactive, onMounted } from 'vue';
import { Dialog, DialogOverlay, DialogTitle, TransitionChild, TransitionRoot } from '#headlessui/vue'
export default {
components:
{
Dialog,
DialogOverlay,
DialogTitle,
TransitionChild,
TransitionRoot,
},
props: {
showCanvas: {
required: true,
type: Boolean
},
},
setup(props, context) {
reactive(props.showCanvas) // Make the prop reactive so that we can watch for changes
let canvas = undefined
watch(() => props.showCanvas, (selection, prevSelection) => {
if (selection == true) {
bindImage()
}
})
function bindImage()
{
canvas = new fabric.Canvas('canvas');
setTimeout(function(){
var rectangle = new fabric.Rect({
width: 300,
height: 200,
fill: '',
stroke: 'green',
strokeWidth: 5
});
console.log("Rectangle>>>>", rectangle);
// Render the Rect in canvas
canvas.add(rectangle);
}, 2000);
}
function closePopup()
{
context.emit("closeCanvas")
}
return {
closePopup,
}
}
}
</script>
<style>
</style>
You should put them into the onMounted block, for example:
<template>
<canvas width="600" height="600" id="canvas" style="border: 1px solid #ccc;"></canvas>
</template>
<script setup>
import { onMounted } from 'vue'
import { fabric } from 'fabric'
function init() {
let canvas = new fabric.Canvas('canvas') // 实例化fabric,并绑定到canvas元素上
// 圆
let circle = new fabric.Circle({
left: 100,
top: 100,
radius: 50,
})
canvas.add(circle)
}
onMounted(() => {
init()
})
</script>

Tailwind UI: Why are transitions only working on enter?

I am using tailwind ui to create a page using Vue.js - v.2
I've looked over this SO thread and I believe I have my transitions in the correct spot.
When I click the menu to show the <MobileSidebar> component, everything works great. When I close the <MobileSidebar> component, the component is just removed from the screen. I do not see the sidebar "slide" off of the browser.
I'm passing the open state as a prop; emitting the status. That seems to be working fine as well.
How can I allow the transition to render before the element is hidden/removed from view?
App.vue
<template>
<MobileSidebar
:open="sidebarOpen"
#toggle-sidebar="toggleSidebar"
/>
</template>
<script>
...
data: () => ({
sidebarOpen: false,
}),
methods: {
toggleSidebar() {
this.sidebarOpen = !this.sidebarOpen;
},
}
</script>
MobileSidebar.vue
<template>
<div
class="fixed inset-0 flex z-40 lg:hidden"
role="dialog"
aria-modal="true"
v-show="open"
>
<transition
enter-active-class="transition-opacity ease-in-out duration-300"
enter-class="opacity-0"
enter-to-class="opacity-100"
leave-active-class="transition-opacity ease-in-out duration-300"
leave-class="opacity-100"
leave-to-class="opacity-0"
>
<div
class="fixed inset-0 bg-gray-600 bg-opacity-75"
aria-hidden="true"
v-show="open"
></div>
</transition>
<transition
enter-active-class="transition ease-in-out duration-300 transform"
enter-class="-translate-x-full"
enter-to-class="translate-x-0"
leave-active-class="transition ease-in-out duration-300 transform"
leave-class="translate-x-0"
leave-to-class="-translate-x-full"
>
<div
class="relative flex-1 flex flex-col max-w-xs w-full bg-white focus:outline-none"
v-show="open"
>
...
</div>
</transition>
</div>
</template>
<script>
...
methods: {
toggleSidebar() {
this.$emit("toggle-sidebar");
},
},
props: {
open: {
type: Boolean,
required: true,
default: false,
},
},
</script>
The issue is still that I didn't have a <transition> wrapping the outter-most div.
This is working properly:
MobileSidebar.vue
<template>
<transition
enter-active-class="transition-opacity ease-linear duration-300"
enter-class="opacity-0"
enter-to-class="opacity-100"
leave-active-class="transition-opacity ease-linear duration-300"
leave-class="opacity-100"
leave-to-class="opacity-0"
>
<div
class="fixed inset-0 flex z-40 lg:hidden"
role="dialog"
aria-modal="true"
v-show="open"
>
...
</div>
</transition>

TinyMce breaking TailwindCss animation when loaded inside a slide-over element

I have a simple Vue3 application that is making heavy use of TailwindUI components. I'm trying to place a TinyMce editor inside of a slide over component and that works fine. The issue is the entry animation.
On first entry, it slides in like it's supposed to. However, if it is closed and reopened the entry animation is gone. The whole time the exit animation continues to work without issue. Is there a way I can do this and keep the animation intact?
Here is a CodeSandBox with the issue reproduced in it's simplest form.
Here is the relevant code:
App.vue
<template>
<button #click="open = true">Open Menu</button>
<SlideOver :open="open" #close="open = false" />
</template>
<script>
import { ref } from "vue";
import SlideOver from "./components/slide-over.vue";
export default {
name: "App",
components: {
SlideOver,
},
setup() {
const open = ref(false);
return { open };
},
};
</script>
slide-over.vue
<!-- This example requires Tailwind CSS v2.0+ -->
<template>
<TransitionRoot as="template" :show="open">
<Dialog
as="div"
static
class="fixed inset-0 overflow-hidden"
#close="$emit('close')"
:open="open"
>
<div class="absolute inset-0 overflow-hidden">
<DialogOverlay class="absolute inset-0" />
<div class="fixed inset-y-0 right-0 pl-10 max-w-full flex">
<TransitionChild
as="template"
enter="transform transition ease-in-out duration-500 sm:duration-700"
enter-from="translate-x-full"
enter-to="translate-x-0"
leave="transform transition ease-in-out duration-500 sm:duration-700"
leave-from="translate-x-0"
leave-to="translate-x-full"
>
<div class="w-screen max-w-md">
<div
class="h-full flex flex-col py-6 bg-white shadow-xl overflow-y-scroll"
>
<div class="px-4 sm:px-6">
<div class="flex items-start justify-between">
<DialogTitle class="text-lg font-medium text-gray-900">
Panel title
</DialogTitle>
<div class="ml-3 h-7 flex items-center">
<button
class="bg-white rounded-md text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
#click="$emit('close')"
>
<span class="sr-only">Close panel</span>
<XIcon class="h-6 w-6" aria-hidden="true" />
</button>
</div>
</div>
</div>
<div class="mt-6 relative flex-1 px-4 sm:px-6">
<TinyMceEditor api-key="no-api-key" />
</div>
</div>
</div>
</TransitionChild>
</div>
</div>
</Dialog>
</TransitionRoot>
</template>
<script>
import {
Dialog,
DialogOverlay,
DialogTitle,
TransitionChild,
TransitionRoot,
} from "#headlessui/vue";
import { XIcon } from "#heroicons/vue/outline";
import TinyMceEditor from "#tinymce/tinymce-vue";
export default {
components: {
Dialog,
DialogOverlay,
DialogTitle,
TransitionChild,
TransitionRoot,
XIcon,
TinyMceEditor,
},
props: {
open: {
type: Boolean,
default: false,
},
},
setup() {},
};
</script>
In my opinion, this is a problem with loading the TinyMce Editor (I don't know exactly what the problem is). I added a delay in loading the editor after opening the modal using watchEffect based on the props open with setTimeout in it and v-if on the TinyMceEditor tag. It may not be a perfect and aesthetic solution, but the animation works smoothly.
Here is a code in codesandbox.io.
And code here: slide-over.vue (App.vue stays the same)
<template>
<TransitionRoot as="template" :show="open">
<Dialog
as="div"
static
class="fixed inset-0 overflow-hidden"
#close="$emit('close')"
:open="open"
>
<div class="absolute inset-0 overflow-hidden">
<DialogOverlay class="absolute inset-0" />
<div class="fixed inset-y-0 right-0 pl-10 max-w-full flex">
<TransitionChild
as="template"
enter="transform transition ease-in-out duration-500 sm:duration-700"
enter-from="translate-x-full"
enter-to="translate-x-0"
leave="transform transition ease-in-out duration-500 sm:duration-700"
leave-from="translate-x-0"
leave-to="translate-x-full"
>
<div class="w-screen max-w-md">
<div
class="h-full flex flex-col py-6 bg-white shadow-xl overflow-y-scroll"
>
<div class="px-4 sm:px-6">
<div class="flex items-start justify-between">
<DialogTitle class="text-lg font-medium text-gray-900">
Panel title
</DialogTitle>
<div class="ml-3 h-7 flex items-center">
<button
class="bg-white rounded-md text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
#click="$emit('close')"
>
<span class="sr-only">Close panel</span>
<XIcon class="h-6 w-6" aria-hidden="true" />
</button>
</div>
</div>
</div>
<div class="mt-6 relative flex-1 px-4 sm:px-6">
<TinyMceEditor v-if="loadEditor" api-key="no-api-key" />
</div>
</div>
</div>
</TransitionChild>
</div>
</div>
</Dialog>
</TransitionRoot>
</template>
<script>
import { ref, watchEffect } from "vue";
import {
Dialog,
DialogOverlay,
DialogTitle,
TransitionChild,
TransitionRoot,
} from "#headlessui/vue";
import { XIcon } from "#heroicons/vue/outline";
import TinyMceEditor from "#tinymce/tinymce-vue";
export default {
components: {
Dialog,
DialogOverlay,
DialogTitle,
TransitionChild,
TransitionRoot,
XIcon,
TinyMceEditor,
},
props: {
open: {
type: Boolean,
default: false,
},
},
setup(props) {
const loadEditor = ref(false);
watchEffect(() => {
if (props.open) {
setTimeout(function () {
loadEditor.value = true;
}, 10);
} else {
loadEditor.value = false;
}
});
return { loadEditor };
}
};
</script>

Vue syntax error: Unexpected Token

I'm, using Vue and Nuxt to build an application, however, I'm getting the following error on compiling the app.
Here is my code:
<template>
<nav class="flex items-center bg-green w-screen justify-between flex-wrap p-6">
<section class="flex items-center flex-no-shrink mr-6">
<nuxt-link to="/" class="block lg:inline-block text-white uppercase lg:mt-0 no-underline mr-4">Ben John Bagley</nuxt-link>
</section>
<section class="block lg:hidden">
<button class="flex items-center p-2" #click="toggle">
<i class="fal fa-bars text-white"></i>
</button>
</section>
<section class="block text-left lg:flex w-full lg:items-center lg:w-auto lg:text-right" :class="open ? 'block': 'hidden'">
<nuxt-link to="/about" class="block mt-3 lg:inline-block text-white lg:mt-0 no-underline mr-4">About</nuxt-link>
<nuxt-link to="/works" class="block mt-3 lg:inline-block text-white lg:mt-0 no-underline mr-4">Works</nuxt-link>
<nuxt-link to="/contact" class="inline-block text-sm px-4 py-2 leading-none text-white border border-green-dark bg-green-dark hover:bg-green-darker hover:border-green-darker rounded no-underline mt-4 mr-4 lg:mt-0">Contact</nuxt-link>
</section>
</nav>
</template>
<script>
export default {
data: function () {
open: false,
},
methods: {
toggle() {
this.open = !this.open
}
}
}
</script>
I'm sure it's a simple fix, however, I'm sure the comma is needed here.
This
data: function () {
open: false,
}
should be
data: function () {
return {
open: false
};
}
data is a function so you have to return a value which in this case is an object.