Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vue 3 (beta) support #131

Closed
taiwoobe opened this issue Oct 23, 2020 · 12 comments
Closed

Vue 3 (beta) support #131

taiwoobe opened this issue Oct 23, 2020 · 12 comments
Labels

Comments

@taiwoobe
Copy link

taiwoobe commented Oct 23, 2020

I installed the fullcalendar library to my Vue 3 project but when I serve the application, I get a warning in my terminal that says ""export 'default' (imported as 'Vue') was not found in 'vue' ".

This crashes the app with the same error when I open the browser. I tried creating the shim.d.ts with the required content but this doesnt still solve the problem.

Here is the vue component script tag.

<script lang="ts">
import { defineComponent } from "vue";
import FullCalendar from "@fullcalendar/vue";
import dayGridPlugin from "@fullcalendar/daygrid";
import interactionPlugin from "@fullcalendar/interaction";

const Calendars = defineComponent({
  components: {
    FullCalendar
  },
  setup() {
    const calendarOptions = {
      plugins: [ dayGridPlugin, interactionPlugin ],
      initialView: "dayGridMonth"
    }

    return { calendarOptions }
  }
});

export default Calendars;
</script>

Is there any workaround for this or is this library not supported with Vue 3?

@taiwoobe taiwoobe changed the title Fullcalendar with Vue 3 support Fullcalendar with Vue 3 & Typescript support Oct 23, 2020
@lchrennew
Copy link

same question!

@acerix acerix changed the title Fullcalendar with Vue 3 & Typescript support Support for Vue 3 Oct 27, 2020
@acerix acerix changed the title Support for Vue 3 Errors with Vue 3 Oct 27, 2020
@acerix
Copy link
Member

acerix commented Oct 27, 2020

It hasn't been updated for Vue 3 yet, the demo also has an error with v3:

https://codesandbox.io/s/keen-margulis-0m3od

@arshaw arshaw added this to the next-release milestone Oct 28, 2020
@arshaw
Copy link
Member

arshaw commented Nov 5, 2020

a PR with Vue v3 support would be very welcome!

@arshaw arshaw removed this from the next-release milestone Nov 5, 2020
@arshaw arshaw added Accepted and removed Confirmed labels Nov 5, 2020
@arshaw arshaw changed the title Errors with Vue 3 Vue 3 (beta) support Nov 5, 2020
@lchrennew
Copy link

lchrennew commented Nov 6, 2020

@arshaw @acerix @kgilden @irustm
I temporarily made a local copy which I've made it works with Vue3(vue 3.0.2). Please check the following code:

  • custom-content-type.js
import { createPlugin } from '@fullcalendar/core'
import { createApp } from 'vue'


/*
wrap it in an object with a `vue` key, which the custom content-type handler system will look for
*/
export function wrapVDomGenerator(vDomGenerator) {
    return function (props) {
        return { vue: vDomGenerator(props) }
    }
}


export const VueContentTypePlugin = createPlugin({
    contentTypeHandlers: {
        vue: buildVDomHandler // looks for the `vue` key
    }
})


function buildVDomHandler() {
    let currentEl
    let v // the Vue instance

    return function (el, vDomContent) { // the handler

        if (currentEl !== el) {
            if (currentEl && v) { // if changing elements, recreate the vue
                v.$destroy()
            }
            currentEl = el
        }

        if (!v) {
            v = initVue(vDomContent)

            // vue's mount method *replaces* the given element. create an artificial inner el
            let innerEl = document.createElement('span')
            el.appendChild(innerEl)
            v.$mount(innerEl)

        } else {
            v.content = vDomContent
        }
    }
}


function initVue(initialContent) {
    return createApp({
        props: {
            content: Array
        },
        propsData: {
            content: initialContent
        },
        render(h) {
            let { content } = this

            // the slot result can be an array, but the returned value of a vue component's
            // render method must be a single node.
            if (content.length === 1) {
                return content[0]

            } else {
                return h('span', {}, content)
            }
        }
    })
}
  • FullCalendar.vue
<template>
  <div :data-fc-render-id="this.renderId"></div>
</template>

<script>
import { Calendar } from '@fullcalendar/core';
import { OPTION_IS_COMPLEX } from './options'
import { shallowCopy, mapHash } from './utils'
import { wrapVDomGenerator, VueContentTypePlugin } from './custom-content-type'

function buildWatchers() {

  let watchers = {

    // watches changes of ALL options and their nested objects,
    // but this is only a means to be notified of top-level non-complex options changes.
    options: {
      deep: true,
      handler(options) {
        let calendar = this.getApi()
        calendar.pauseRendering()
        calendar.resetOptions(this.buildOptions(options))
        this.renderId++ // will queue a rerender
      }
    }
  }

  for (const complexOptionName in OPTION_IS_COMPLEX) {

    // handlers called when nested objects change
    watchers[`options.${complexOptionName}`] = {
      deep: true,
      handler(val) {

        // unfortunately the handler is called with undefined if new props were set, but the complex one wasn't ever set
        if (val !== undefined) {

          let calendar = this.getApi()
          calendar.pauseRendering()
          calendar.resetOptions({
            [complexOptionName]: shallowCopy(val)
          }, true)

          this.renderId++
        }
      }
    }
  }

  return watchers
}

export default {
  name: 'FullCalendar',
  props: ['options'],
  data() {
    return {
      renderId: 0
    }
  },
  mounted() {
    const internal = this.$options;
    internal.scopedSlotOptions = mapHash(this.$scopedSlots, wrapVDomGenerator); // needed for buildOptions
    const calendar = new Calendar(this.$el, this.buildOptions(this.options));
    internal.calendar = calendar;
    calendar.render();
  },
  methods: {
    getApi() {
      return this.$options.calendar;
    },
    buildOptions(suppliedOptions) {
      let internal = this.$options
      suppliedOptions = suppliedOptions || {}
      return {
        ...internal.scopedSlotOptions,
        ...suppliedOptions, // spread will pull out the values from the options getter functions
        plugins: (suppliedOptions.plugins || []).concat([
          VueContentTypePlugin
        ])
      }
    },
  },
  beforeUpdate() {
    this.getApi().resumeRendering(); // the watcher handlers paused it
  },
  beforeUnmount() {
    this.getApi().destroy();
  },
  watch: buildWatchers()
}
</script>

<style scoped>

</style>
  • options.js
export const OPTION_IS_COMPLEX = {
    headerToolbar: true,
    footerToolbar: true,
    events: true,
    eventSources: true,
    resources: true
}
  • utils.js
// TODO: add types!


/*
works with objects and arrays
*/
export function shallowCopy(val) {
    if (typeof val === 'object') {
        if (Array.isArray(val)) {
            val = Array.prototype.slice.call(val)
        } else if (val) { // non-null
            val = { ...val }
        }
    }

    return val
}


export function mapHash(input, func) {
    const output: any = {}

    for (const key in input) {
        if (input.hasOwnProperty(key)) {
            output[key] = func(input[key], key)
        }
    }

    return output
}

@izadiegizabal
Copy link

izadiegizabal commented Nov 20, 2020

I'm also having the same troubles making the plugin work under Vue 3, I've tried to emulate what @lchrennew is suggesting but I got the error Please import the top-level fullcalendar lib before attempting to import a plugin.

Hope someone can provide a Vue 3 compatible PR soon 😢.

@shadoWalker89
Copy link

Hi, dear full calendar team, since v3 was already released, is there any plans to support vue 3 soon ?
Thanks

@pikax
Copy link

pikax commented Feb 5, 2021

I have this simple component to make fullcalendar work on vue3 + vite

// needed to make sure the `vdom` loads before everything else - fix for vite
import "@fullcalendar/core/vdom";
import { Calendar, CalendarOptions } from "@fullcalendar/core";
import {
  defineComponent,
  h,
  onMounted,
  onUnmounted,
  ref,
  watchEffect,
} from "vue";

export default defineComponent({
  props: {
    options: Object as () => CalendarOptions,
  },

  setup(props) {
    const el = ref<HTMLElement>();

    const calendar = ref<Calendar>();

    onMounted(() => {
      calendar.value = new Calendar(el.value!, props.options);
      calendar.value.render();
    });

    watchEffect(() => {
      if (calendar.value) {
        calendar.value.pauseRendering();
        calendar.value.resetOptions(props.options);
        calendar.value.resumeRendering();
      }
    });

    onUnmounted(() => {
      calendar.value?.destroy();
    });

    return () => h("div", { ref: el });
  },
});

@shadoWalker89
Copy link

Yeah I did something similar I used vanilla js fullcalendar within a vue component and use watchers to update options on the calendar

@kristofgilicze
Copy link

I can confirm that the wrapper provided by @pikax works great on vue3 with vite.

@beau-gosse
Copy link

@pikax this is incredible. Thanks a lot for sharing!

With that setup, it's easy to even pass back the Calendar Api to the parent with a simple event:
emit("onLoaded", calendar.value);

Well done!

@jfrag
Copy link

jfrag commented Mar 25, 2021

Hi,

It work with the code from @lchrennew !

i just have a problem with the v-slot:eventContent. it didn't work with the trick.
Have you any idea ?

@arshaw
Copy link
Member

arshaw commented Jun 2, 2021

There is now an official Vue 3 package:
https://www.npmjs.com/package/@fullcalendar/vue3

I've retrofitted the existing docs to mention Vue3:
https://fullcalendar.io/docs/vue

Please post bugs/requests as separate tickets!

@arshaw arshaw closed this as completed Jun 2, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

10 participants