0

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>
Jmh2013
  • 2,625
  • 2
  • 26
  • 42

1 Answers1

1

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>
  • 1
    I agree that it is an issue with how TinyMCE is loaded into the page. However, the slight delay does seem to address the issue I was having. Although, it's not ideal it's the best option I have at the moment. Until I figure out a better solution, that's what I'm going with. Thanks! – Jmh2013 Jul 28 '21 at 18:23