quasarframework / quasar-ui-qcalendar

QCalendar - Quasar App Extension, Vue CLI plug-in and UMD distributions available
https://quasarframework.github.io/quasar-ui-qcalendar
MIT License
421 stars 116 forks source link

On the initial page load mouse events don't fire #426

Open asyahubar opened 10 months ago

asyahubar commented 10 months ago

The bug I have a quasar application where I need to use both QCalendarDay and QCalendarAgenda. On the initial page load, components seem to load just fine, visually it's ok but mouse events don't fire at all. The only error in the console I get is when I navigate to the page with my q-calendar components from some other pages. The error is:

Uncaught (in promise) TypeError: Cannot read properties of null (reading 'offsetWidth') QTabs.js:181:1

This is the function on line 181 that causes the error: qcalendar-error q-calendar-error-trace

My theory I assume you use QTabs for changing calendar view and on navigation the function recalculateScroll() is called before the component is mounted. I use QTabs on another page in my application and don't get the same problem.

My component for q-calendar handling looks like this.

<template>
    <div>
            <QCalendarDay
                v-if="selectedCalendar === 'day'"
                ref="calendarDay"
                v-model="selectedDate"
                v-model:model-tasks="sessions"
                :selected-start-end-dates="startEndDates"
                :view="selectedView"
                :weekdays="[1, 2, 3, 4, 5, 6, 0]"
                :interval-minutes="15"
                :interval-count="96"
                :interval-height="15"
                time-clicks-clamped
                hour24Format
                animated
                bordered
                hoverable
                focusable
                @click-date="onClickDate"
                @click-time="onClickTime"
                @mousedown-time="onMouseDownTime"
                @mouseup-time="onMouseUpTime"
                @mousemove-time="onMouseMoveTime"
            >
            </QCalendarDay>

            <QCalendarAgenda
                v-if="selectedCalendar === 'agenda'"
                ref="calendarAgenda"
                v-model="selectedDate"
                v-model:model-tasks="sessions"
                :view="selectedView"
                :weekdays="[1, 2, 3, 4, 5, 6, 0]"
                :left-column-options="trainersColumn"
                :interval-minutes="15"
                :interval-count="96"
                :interval-height="15"
                column-options-id="id"
                column-options-label="label"
                time-clicks-clamped
                hour24Format
                animated
                bordered
                hoverable
                focusable
                @click-date="onClickDate"
            >
            </QCalendarAgenda>
    </div>
</template>
<script setup>
import {
    defineProps,
    defineEmits,
    defineExpose,
    computed,
    ref,
    watch,
} from "vue";
import { useI18n } from "vue-i18n";
import { useQuasar } from "quasar";
import { useMouse } from "src/composables/mouse";
import {
    QCalendarDay,
    QCalendarAgenda,
    today,
    getDateTime,
    getDayTimeIdentifier,
} from "@quasar/quasar-ui-qcalendar/src/index.js";

import "@quasar/quasar-ui-qcalendar/src/QCalendarVariables.sass";
import "@quasar/quasar-ui-qcalendar/src/QCalendarTransitions.sass";
import "@quasar/quasar-ui-qcalendar/src/QCalendar.sass";

const props = defineProps({
    selectedCalendar: {
        type: String,
        default: "day",
    },
    selectedView: {
        type: String,
        default: "week",
    },
    sessions: {
        type: Array,
        default: [],
    },
    startEndDates: Array,
});

const emit = defineEmits([
    "requestCreateSession",
    "update:startEndDates",
    "update:selectedDate",
]);

// Stores
const q = useQuasar();
const { t } = useI18n();

// Composables
const { isLeftClick } = useMouse();

// Additional
const trainersColumn = [
    {
        id: "trainers",
        label: t("sidebar.trainers"),
    },
];

// Data
const sessions = computed(() => props.sessions);
const calendarDay = ref(null);
const calendarAgenda = ref(null);
const selectedDate = ref(today());
const anchorTimestamp = ref(null);
const endTimestamp = ref(null);
const mouseDown = ref(false);

const startEndDates = computed(() => {
    const dates = [];
    const anchorDayTimeIdentifier = !!anchorTimestamp.value
        ? getDayTimeIdentifier(anchorTimestamp.value)
        : false;
    const endDayTimeIdentifier = !!endTimestamp.value
        ? getDayTimeIdentifier(endTimestamp.value)
        : false;

    if (!!anchorDayTimeIdentifier && !!endDayTimeIdentifier) {
        if (anchorDayTimeIdentifier <= endDayTimeIdentifier) {
            dates.push(
                getDateTime(anchorTimestamp.value),
                getDateTime(endTimestamp.value)
            );
        } else {
            dates.push(
                getDateTime(endTimestamp.value),
                getDateTime(anchorTimestamp.value)
            );
        }
    }

    return dates;
});

// Watchers
watch(startEndDates, (newDates, oldDates) => {
    emit("update:startEndDates", startEndDates);
});
watch(selectedDate, (newDate, oldDate) => {
    emit("update:selectedDate", newDate);
});

watch(calendarDay, () => {
    console.log('calendarDay', calendarDay.value)
})

// Methods
const setToday = () => {
    calendarDay.value.moveToToday();
    calendarAgenda.value.moveToToday();
};
const setPrev = () => {
    calendarDay.value.prev();
    calendarAgenda.value.prev();
};
const setNext = () => {
    calendarDay.value.next();
    calendarAgenda.value.next();
};

const onClickDate = (data) => {
    console.log("onClickDate", data);
};
const onClickTime = ({ scope }) => {
    console.log("onClickTime", scope);
};
const onMouseDownTime = ({ scope, event }) => {
    console.log("onMouseDownTime", { scope, event });
    if (isLeftClick(event)) {
        if (
            anchorTimestamp.value !== null &&
            endTimestamp.value !== null &&
            getDateTime(anchorTimestamp.value) ===
                getDateTime(endTimestamp.value)
        ) {
            endTimestamp.value = scope.timestamp;
            mouseDown.value = false;
        }
        mouseDown.value = true;
        anchorTimestamp.value = scope.timestamp;
        endTimestamp.value = scope.timestamp;
    }
};
const onMouseUpTime = ({ scope, event }) => {
    console.log("onMouseUpTime", { scope, event });
    if (isLeftClick(event)) {
        // mouse is up, capture last and cancel selection
        endTimestamp.value = scope.timestamp;
        mouseDown.value = false;
    }
    if (scope.timestamp.future) {
        emit("requestCreateSession");
    }
};
const onMouseMoveTime = ({ scope }) => {
    if (!!mouseDown.value) {
        endTimestamp.value = scope.timestamp;
    }
};

defineExpose({
    setToday,
    setPrev,
    setNext,
});
</script>

Please note that on calendar view change, ref value get changed and mouse events start to work but I expect them to work from the start. My default calendar mode is day. So when I change to agenda, agenda works. And when I change the mode back to day, day also start to work.

My setup: