calendar icon indicating copy to clipboard operation
calendar copied to clipboard

Restrict Event Dropping for Specific Time Ranges

Open harishLaravel opened this issue 10 months ago • 6 comments

Hello,

I am trying to restrict event dropping in EventCalendar for specific time ranges on a particular date. While I can successfully prevent events from being dropped for an entire day, I am unable to enforce restrictions for only a specific time window.

For example, if an employee takes a half-day leave in the morning (from 12 AM to 12 PM), I want to prevent events from being dropped within that time range while still allowing events to be dropped after 12 PM until the end of the day. In FullCalendar, the eventAllow method worked for this purpose. Could you please suggest an alternative approach to achieve this functionality in EventCalendar?

and this is my code ,

window.drop = function(event) { event.preventDefault(); console.log("drop event log " + event);

                // Retrieve the dragged event data
                const data = JSON.parse(event.dataTransfer.getData("application/json"));
                console.log("Dropped Event Data:", data);

                const x = event.clientX;
                const y = event.clientY;

                // console.warn("⚠️ Using `document.elementFromPoint()` to determine drop target.");

                // Find the nearest calendar slot where the event was dropped
                let targetElement = document.elementFromPoint(x, y);
                console.log("tarr ", targetElement);

                if (!targetElement) {
                   
                    return;
                }
                if (targetElement && targetElement.textContent.trim() === 'Holiday') {
                    alert("cannot assign work on holidays")
                    return false;
                }
                else if (targetElement && targetElement.textContent.trim() === 'Leave') {
                    alert("cannot assign work on Leaves");
                    return false;
                }

                console.log("✅ Target Drop Element:", targetElement);

                // Find the closest `.ec-day`
                let dayElement = targetElement.closest(".ec-day");
                if (!dayElement) {
                    // console.error("❌ Error: Could not determine the drop date. No `.ec-day` found.");
                    return;
                }
                // console.log("✅ Found day element:", dayElement);

                // Move up to `.ec-container` or `.ec-main` to find `.ec-day-head`
                let container = dayElement.closest(".ec-container, .ec-main, .ec-days");
                if (!container) {
                    console.error(
                        "❌ Error: Could not find `.ec-container` or `.ec-main` to locate `.ec-day-head`.");
                    return;
                }

                // console.log("✅ Found calendar container:", container);

                // Find the `.ec-day-head` elements
                let allDayHeads = [...document.querySelectorAll(".ec-day-head time[datetime]")];
                // console.log("allDaysHeads ", allDayHeads);

                // console.log("✅ Checking available elements in the document...");
                // console.log([...document.querySelectorAll(".ec-day-head")]);

                if (allDayHeads.length === 0) {
                    // console.error("❌ Error: `.ec-day-head time[datetime]` not found anywhere.");
                    return;
                }

                // Find the matching `.ec-day-head` based on `.ec-day` index
                let allDays = [...container.querySelectorAll(".ec-day")]; // Get all day columns
                let dayIndex = allDays.indexOf(dayElement); // Get index of dropped day

                if (dayIndex === -1 || dayIndex >= allDayHeads.length) {
                    // console.error("❌ Error: Could not determine the correct `.ec-day-head`.");
                    return;
                }

                let dateElement = allDayHeads[dayIndex]; // Match index with day head
                let dateStr = dateElement.getAttribute("datetime");
                // console.log("✅ Extracted Date:", dateStr);

                // Find the closest `.ec-time` to get the correct time slot
                let timeElement = targetElement.closest(".ec-time");
                let dateStart;

                if (!timeElement) {
                    // console.warn("⚠️ No exact time slot found. Defaulting to 9:00 AM.");
                    dateStart = new Date(`${dateStr}T09:00:00`);
                } else {
                    // Extract the exact time from the `.ec-time time[datetime]`
                    let timeSlot = timeElement.querySelector("time[datetime]");
                    if (!timeSlot) {
                        // console.warn("⚠️ No time datetime attribute found. Defaulting to 9:00 AM.");
                        dateStart = new Date(`${dateStr}T09:00:00`);
                    } else {
                        dateStart = new Date(timeSlot.getAttribute("datetime"));
                    }
                }
                // Define event duration (default 2 hours)
                const dateEnd = new Date(dateStart);
                dateEnd.setHours(dateEnd.getHours() + 2);

                // Assign a default resourceId (Modify this based on your setup)
                let allResources = [...document.querySelectorAll(
                    ".ec-resource-label")]; // Get all resource elements
                console.log("📌 All Resource Elements:", allResources);

                let resourceElement = null;

                // **Loop through each `.ec-resource-label` to check which is closest**
                for (let resource of allResources) {
                    let rect = resource.getBoundingClientRect();
                    if (y >= rect.top && y <= rect.bottom) {
                        resourceElement = resource;
                        break; // Stop once the closest resource is found
                    }
                }
                console.log(resourceElement);

                console.log("🔍 Found Resource Element:", resourceElement);

                let newResourceId = resourceElement ? resourceElement.getAttribute("data-resource-id") : null;
                console.log("✅ Extracted Resource ID:", newResourceId);

                // Fallback if no resource ID is found
                if (!newResourceId) {
                    console.warn("⚠️ No resource ID found. Defaulting to first employee.");
                    newResourceId = resources.length > 0 ? resources[0].id : "default_resource";
                }

                console.log("🎯 Final Resource ID:", newResourceId);
                const isHoliday = dayElement.classList.contains("holiday");
                const newEvent = {
                    id: data.id,
                    title: data.title,
                    start: dateStart,
                    end: dateEnd,
                    resourceIds: [newResourceId],
                    extendedProps: {
                        client_id: data.client_id,
                        isHoliday: isHoliday
                    }
                };

                // console.log("✅ Replacing event with new data:", newEvent);

                // Add the new event by resetting the events list
                // existingEvents.push(updatedEvent);
                // if (calendar.setEvents) {
                //     calendar.updateEvent(existingEvents);
                //     console.log("✅ Events updated successfully via `setEvents()`.");
                // } else if (calendar.update) {
                //     calendar.update({
                //         events: existingEvents
                //     });
                //     console.log("✅ Events updated successfully via `update()`.");
                // } else {
                //     console.warn("⚠️ No direct update method found. Consider reloading the calendar.");
                // }

                if (window.eventCalendar && typeof window.eventCalendar.addEvent === "function") {
                    window.eventCalendar.addEvent(newEvent);
                    // console.log("✅ Event added successfully.", newEvent);
                } else {
                    // console.error("❌ Error: `eventCalendar.addEvent` is not available.");
                    // console.log("🔍 Available methods:", Object.keys(window.eventCalendar || {}));
                }

                const jobData = {
                    id: data.id,
                    title: data.title,
                    client_id: data.client_id,
                    start: convertToISO(dateStart),
                    end: convertToISO(dateEnd),
                    resource_id: newResourceId // Optional, modify based on your system
                };

                // console.log("📤 Sending job data to backend:", jobData);
                createJobSheet(jobData);
            }

Thank you.

harishLaravel avatar Feb 11 '25 13:02 harishLaravel

Currently, only the prevent() function is available in the eventDrop callback. This function allows you to cancel the drag if necessary.

eventAllow may be implemented in future versions of the Event Calendar.

vkurko avatar Feb 17 '25 08:02 vkurko

Thanks for your response.

I can prevent events from being scheduled on specific dates, but I am unable to restrict event drops for certain time slots. Currently, I am trying to retrieve the exact datetime from the.ec-times element. While I can access the datetime, I am unable to determine the precise time slot where the event is dropped.

harishLaravel avatar Feb 17 '25 08:02 harishLaravel

I am unable to determine the precise time slot where the event is dropped

The eventDrop callback contains this information. You are given event that already contains the new start and end, and the oldEvent where these are the values ​​before the drag.

vkurko avatar Feb 17 '25 08:02 vkurko

thank you once again. I will try and update here

harishLaravel avatar Feb 17 '25 08:02 harishLaravel

Hi,

I am currently facing an issue with the allowDrop method. How can I retrieve the start and end datetime of an external event when attempting to drop it onto the calendar? Which method in the documentation provides access to the dates of an external event?

and this the method i used

window.allowDrop = function(event) { event.preventDefault(); // Allow dropping

                // Find the closest `.ec-day` where the event was dropped
                let targetDay = event.target.closest(".ec-day");

                if (!targetDay) {
                    console.log("❌ No `.ec-day` found.");
                    return;
                }
                let x = event.clientX;
                let y = event.clientY
                console.log("✅ Found Target `.ec-day`:", targetDay);

                // Get the index of `.ec-day`
                let allDays = [...document.querySelectorAll(".ec-day")];
                let dayIndex = allDays.indexOf(targetDay);

                if (dayIndex === -1) {
                    console.log("❌ Could not find `.ec-day` index.");
                    return;
                }

                // Find `.ec-header`
                let headerContainer = document.querySelector(".ec-header");

                if (!headerContainer) {
                    console.log("❌ No `.ec-header` found.");
                    return;
                }

                console.log("✅ Found `.ec-header`: ", headerContainer);

                // Get all time slots from `.ec-times`
                let allTimeHeaders = [...headerContainer.querySelectorAll(".ec-times time[datetime]")];

                console.log("📌 Found Time Elements:", allTimeHeaders.length, allTimeHeaders);

                if (allTimeHeaders.length === 0) {
                    console.log("❌ No `.ec-times time[datetime]` elements found in `.ec-header`.");
                    return;
                }

                // Get mouse Y position relative to `.ec-day`
                let dayRect = targetDay.getBoundingClientRect();
                let mouseY = event.clientY - dayRect.top; // Mouse Y relative to `.ec-day`

                // Get all `.ec-time` elements (they define the hourly slots)
                let allTimeRows = [...document.querySelectorAll(".ec-time")];

                let matchedTime = null;

                // Loop through `.ec-time` rows to find the closest match
                for (let timeRow of allTimeRows) {
                    let rowRect = timeRow.getBoundingClientRect();
                    console.log(rowRect);
                    
                    if (y >= rowRect.top && y <= rowRect.bottom) {
                        matchedTime = timeRow.querySelector("time[datetime]");
                        console.log(`✅ Matched Time Row - X: ${rowRect.left}, Y: ${rowRect.top}`);
                        break;
                    }
                }


                if (!matchedTime || !matchedTime.hasAttribute("datetime")) {
                    console.log("❌ No `datetime` found in `.ec-time`.");
                    return;
                }

                // Extract the `datetime` attribute
                let dropStartDate = new Date(matchedTime.getAttribute("datetime"));

                console.log("📅 Dropped on Date & Time (Start):", dropStartDate.toISOString());

                // Store the drop start date globally for use in `window.drop`
                window.droppedStartDate = dropStartDate.toISOString();
            }; 

harishLaravel avatar Feb 17 '25 12:02 harishLaravel

external event when attempting to drop it onto the calendar

Event Calendar does not support dragging and dropping external events into the calendar.

vkurko avatar Feb 17 '25 12:02 vkurko

hi mate !

hope you well

Any updates on external events allowing in the event calendar?

harishLaravel avatar Mar 29 '25 10:03 harishLaravel

Please see the new dragConstraint option.

For external drop.you can use dateFromPoint method, see #145 .

vkurko avatar Apr 01 '25 20:04 vkurko

Hi, when we drag an external event into the calendar and try to place it in a specific time slot, is it possible to capture that time?

harishLaravel avatar Apr 16 '25 11:04 harishLaravel

Hi, when we drag an external event into the calendar and try to place it in a specific time slot, is it possible to capture that time?

You can try to use dateFromPoint method which gives you information about the time slot for any cursor position.

vkurko avatar Apr 16 '25 14:04 vkurko