MDL-59394 calendar: add drag and drop between months
authorRyan Wyllie <ryan@moodle.com>
Thu, 24 Aug 2017 08:20:35 +0000 (08:20 +0000)
committerRyan Wyllie <ryan@moodle.com>
Wed, 30 Aug 2017 02:42:06 +0000 (02:42 +0000)
19 files changed:
calendar/amd/build/calendar.min.js
calendar/amd/build/drag_drop.min.js [deleted file]
calendar/amd/build/drag_drop_data_store.min.js [new file with mode: 0644]
calendar/amd/build/month_navigation_drag_drop.min.js [new file with mode: 0644]
calendar/amd/build/month_view_drag_drop.min.js [new file with mode: 0644]
calendar/amd/build/view_manager.min.js
calendar/amd/src/calendar.js
calendar/amd/src/drag_drop_data_store.js [new file with mode: 0644]
calendar/amd/src/month_navigation_drag_drop.js [new file with mode: 0644]
calendar/amd/src/month_view_drag_drop.js [moved from calendar/amd/src/drag_drop.js with 64% similarity]
calendar/amd/src/view_manager.js
calendar/lib.php
calendar/templates/month_detailed.mustache
calendar/templates/month_navigation.mustache
theme/boost/scss/moodle/calendar.scss
theme/bootstrapbase/less/moodle/bs4-compat.less
theme/bootstrapbase/less/moodle/calendar.less
theme/bootstrapbase/less/moodle/core.less
theme/bootstrapbase/style/moodle.css

index c797403..5167149 100644 (file)
Binary files a/calendar/amd/build/calendar.min.js and b/calendar/amd/build/calendar.min.js differ
diff --git a/calendar/amd/build/drag_drop.min.js b/calendar/amd/build/drag_drop.min.js
deleted file mode 100644 (file)
index 249c6d6..0000000
Binary files a/calendar/amd/build/drag_drop.min.js and /dev/null differ
diff --git a/calendar/amd/build/drag_drop_data_store.min.js b/calendar/amd/build/drag_drop_data_store.min.js
new file mode 100644 (file)
index 0000000..e2cc7de
Binary files /dev/null and b/calendar/amd/build/drag_drop_data_store.min.js differ
diff --git a/calendar/amd/build/month_navigation_drag_drop.min.js b/calendar/amd/build/month_navigation_drag_drop.min.js
new file mode 100644 (file)
index 0000000..a8888bf
Binary files /dev/null and b/calendar/amd/build/month_navigation_drag_drop.min.js differ
diff --git a/calendar/amd/build/month_view_drag_drop.min.js b/calendar/amd/build/month_view_drag_drop.min.js
new file mode 100644 (file)
index 0000000..b2ee13d
Binary files /dev/null and b/calendar/amd/build/month_view_drag_drop.min.js differ
index dc730d3..bb8c146 100644 (file)
Binary files a/calendar/amd/build/view_manager.min.js and b/calendar/amd/build/view_manager.min.js differ
index e75671c..5231a6c 100644 (file)
@@ -168,24 +168,30 @@ define([
      * updated.
      *
      * @param {event} e The calendar move event
-     * @param {object} eventElement The jQuery element with the event id
-     * @param {object} originElement The jQuery element for where the event is moving from
+     * @param {int} eventId The event id being moved
+     * @param {object|null} originElement The jQuery element for where the event is moving from
      * @param {object} destinationElement The jQuery element for where the event is moving to
      */
-    var handleMoveEvent = function(e, eventElement, originElement, destinationElement) {
-        var eventId = eventElement.attr('data-event-id');
-        var originTimestamp = originElement.attr('data-day-timestamp');
+    var handleMoveEvent = function(e, eventId, originElement, destinationElement) {
+        var originTimestamp = null;
         var destinationTimestamp = destinationElement.attr('data-day-timestamp');
 
+        if (originElement) {
+            originTimestamp = originElement.attr('data-day-timestamp');
+        }
+
         // If the event has actually changed day.
-        if (originTimestamp != destinationTimestamp) {
+        if (!originElement || originTimestamp != destinationTimestamp) {
             Templates.render('core/loading', {})
                 .then(function(html, js) {
                     // First we show some loading icons in each of the days being affected.
-                    originElement.find(SELECTORS.DAY_CONTENT).addClass('hidden');
                     destinationElement.find(SELECTORS.DAY_CONTENT).addClass('hidden');
-                    Templates.appendNodeContents(originElement, html, js);
                     Templates.appendNodeContents(destinationElement, html, js);
+
+                    if (originElement) {
+                        originElement.find(SELECTORS.DAY_CONTENT).addClass('hidden');
+                        Templates.appendNodeContents(originElement, html, js);
+                    }
                     return;
                 })
                 .then(function() {
@@ -195,19 +201,21 @@ define([
                 .then(function() {
                     // If the update was successful then broadcast an event letting the calendar
                     // know that an event has been moved.
-                    $('body').trigger(CalendarEvents.eventMoved, [eventElement, originElement, destinationElement]);
+                    $('body').trigger(CalendarEvents.eventMoved, [eventId, originElement, destinationElement]);
                     return;
                 })
                 .always(function() {
                     // Always remove the loading icons regardless of whether the update
                     // request was successful or not.
-                    var originLoadingElement = originElement.find(SELECTORS.LOADING_ICON);
                     var destinationLoadingElement = destinationElement.find(SELECTORS.LOADING_ICON);
-                    originElement.find(SELECTORS.DAY_CONTENT).removeClass('hidden');
                     destinationElement.find(SELECTORS.DAY_CONTENT).removeClass('hidden');
-
-                    Templates.replaceNode(originLoadingElement, '', '');
                     Templates.replaceNode(destinationLoadingElement, '', '');
+
+                    if (originElement) {
+                        var originLoadingElement = originElement.find(SELECTORS.LOADING_ICON);
+                        originElement.find(SELECTORS.DAY_CONTENT).removeClass('hidden');
+                        Templates.replaceNode(originLoadingElement, '', '');
+                    }
                     return;
                 })
                 .fail(Notification.exception);
@@ -265,7 +273,7 @@ define([
         body.on(CalendarEvents.moveEvent, handleMoveEvent);
         // When an event is successfully moved we should updated the UI.
         body.on(CalendarEvents.eventMoved, function() {
-            window.location.reload();
+            CalendarViewManager.reloadCurrentMonth(root);
         });
 
         eventFormModalPromise.then(function(modal) {
diff --git a/calendar/amd/src/drag_drop_data_store.js b/calendar/amd/src/drag_drop_data_store.js
new file mode 100644 (file)
index 0000000..6414f8e
--- /dev/null
@@ -0,0 +1,95 @@
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * A javascript module to store calendar drag and drop data.
+ *
+ * This module is unfortunately required because of the limitations
+ * of the HTML5 drag and drop API and it's ability to provide data
+ * between the different stages of the drag/drop lifecycle.
+ *
+ * @module     core_calendar/drag_drop_data_store
+ * @package    core_calendar
+ * @copyright  2017 Ryan Wyllie <ryan@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+define([], function() {
+    /* @var {int|null} eventId The id of the event being dragged */
+    var eventId = null;
+    /* @var {int|null} durationDays How many days the event spans */
+    var durationDays = null;
+
+    /**
+     * Store the id of the event being dragged.
+     *
+     * @param {int} id The event id
+     */
+    var setEventId = function(id) {
+        eventId = id;
+    };
+
+    /**
+     * Get the stored event id.
+     *
+     * @return {int|null}
+     */
+    var getEventId = function() {
+        return eventId;
+    };
+
+    /**
+     * Check if the store has an event id.
+     *
+     * @return {bool}
+     */
+    var hasEventId = function() {
+        return eventId !== null;
+    };
+
+    /**
+     * Store the duration (in days) of the event being dragged.
+     *
+     * @param {int} days Number of days the event spans
+     */
+    var setDurationDays = function(days) {
+        durationDays = days;
+    };
+
+    /**
+     * Get the stored number of days.
+     *
+     * @return {int|null}
+     */
+    var getDurationDays = function() {
+        return durationDays;
+    };
+
+    /**
+     * Reset all of the stored values.
+     */
+    var clearAll = function() {
+        setEventId(null);
+        setDurationDays(null);
+    };
+
+    return {
+        setEventId: setEventId,
+        getEventId: getEventId,
+        hasEventId: hasEventId,
+        setDurationDays: setDurationDays,
+        getDurationDays: getDurationDays,
+        clearAll: clearAll
+    };
+});
diff --git a/calendar/amd/src/month_navigation_drag_drop.js b/calendar/amd/src/month_navigation_drag_drop.js
new file mode 100644 (file)
index 0000000..0e192df
--- /dev/null
@@ -0,0 +1,221 @@
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * A javascript module to handle calendar drag and drop in the calendar
+ * month view navigation.
+ *
+ * This code is run each time the calendar month view is re-rendered. We
+ * only register the event handlers once per page load so that the in place
+ * DOM updates that happen on month change don't continue to register handlers.
+ *
+ * @module     core_calendar/month_navigation_drag_drop
+ * @class      month_navigation_drag_drop
+ * @package    core_calendar
+ * @copyright  2017 Ryan Wyllie <ryan@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+define([
+            'jquery',
+            'core_calendar/drag_drop_data_store',
+        ],
+        function(
+            $,
+            DataStore
+        ) {
+
+    var SELECTORS = {
+        DRAGGABLE: '[draggable="true"][data-region="event-item"]',
+        DROP_ZONE: '[data-drop-zone="nav-link"]',
+    };
+    var HOVER_CLASS = 'bg-primary text-white';
+    var TARGET_CLASS = 'drop-target';
+    var HOVER_TIME = 1000; // 1 second hover to change month.
+
+    // We store some static variables at the module level because this
+    // module is called each time the calendar month view is reloaded but
+    // we want some actions to only occur ones.
+
+    /* @var {bool} registered If the event listeners have been added */
+    var registered = false;
+    /* @var {int} hoverTimer The timeout id of any timeout waiting for hover */
+    var hoverTimer = null;
+    /* @var {object} root The root nav element we're operating on */
+    var root = null;
+
+    /**
+     * Add or remove the appropriate styling to indicate whether
+     * the drop target is being hovered over.
+     *
+     * @param {object} target The target drop zone element
+     * @param {bool} hovered If the element is hovered over ot not
+     */
+    var updateHoverState = function(target, hovered) {
+        if (hovered) {
+            target.addClass(HOVER_CLASS);
+        } else {
+            target.removeClass(HOVER_CLASS);
+        }
+    };
+
+    /**
+     * Add some styling to the UI to indicate that the nav links
+     * are an acceptable drop target.
+     */
+    var addDropZoneIndicator = function() {
+        root.find(SELECTORS.DROP_ZONE).addClass(TARGET_CLASS);
+    };
+
+    /**
+     * Remove the styling from the nav links.
+     */
+    var removeDropZoneIndicator = function() {
+        root.find(SELECTORS.DROP_ZONE).removeClass(TARGET_CLASS);
+    };
+
+    /**
+     * Get the drop zone target from the event, if one is found.
+     *
+     * @param {event} e Javascript event
+     * @return {object|null}
+     */
+    var getTargetFromEvent = function(e) {
+        var target = $(e.target).closest(SELECTORS.DROP_ZONE);
+        return (target.length) ? target : null;
+    };
+
+    /**
+     * This will add a visual indicator to the calendar UI to
+     * indicate which nav link is a valid drop zone.
+     */
+    var dragstartHandler = function(e) {
+        // Make sure the drag event is for a calendar event.
+        var eventElement = $(e.target).closest(SELECTORS.DRAGGABLE);
+
+        if (eventElement.length) {
+            addDropZoneIndicator();
+        }
+    };
+
+    /**
+     * Update the hover state of the target nav element when
+     * the user is dragging an event over it.
+     *
+     * This will add a visual indicator to the calendar UI to
+     * indicate which nav link is being hovered.
+     *
+     * @param {event} e The dragover event
+     */
+    var dragoverHandler = function(e) {
+        e.preventDefault();
+        var target = getTargetFromEvent(e);
+
+        if (!target) {
+            return;
+        }
+
+        // If we're not draggin a calendar event then
+        // ignore it.
+        if (!DataStore.hasEventId()) {
+            return;
+        }
+
+        if (!hoverTimer) {
+            hoverTimer = setTimeout(function() {
+                target.click();
+                hoverTimer = null;
+            }, HOVER_TIME);
+        }
+
+        updateHoverState(target, true);
+        removeDropZoneIndicator();
+    };
+
+    /**
+     * Update the hover state of the target nav element that was
+     * previously dragged over but has is no longer a drag target.
+     *
+     * This will remove the visual indicator from the calendar UI
+     * that was added by the dragoverHandler.
+     *
+     * @param {event} e The dragstart event
+     */
+    var dragleaveHandler = function(e) {
+        var target = getTargetFromEvent(e);
+
+        if (!target) {
+            return;
+        }
+
+        if (hoverTimer) {
+            clearTimeout(hoverTimer);
+            hoverTimer = null;
+        }
+
+        updateHoverState(target, false);
+        addDropZoneIndicator();
+        e.preventDefault();
+    };
+
+    /**
+     * Remove the visual indicator from the calendar UI that was
+     * added by the dragoverHandler.
+     *
+     * @param {event} e The drop event
+     */
+    var dropHandler = function(e) {
+        removeDropZoneIndicator();
+        var target = getTargetFromEvent(e);
+
+        if (!target) {
+            return;
+        }
+
+        updateHoverState(target, false);
+        e.preventDefault();
+    };
+
+    return {
+        /**
+         * Initialise the event handlers for the drag events.
+         *
+         * @param {object} rootElement The element containing calendar nav links
+         */
+        init: function(rootElement) {
+            // Only register the handlers once on the first load.
+            if (!registered) {
+                // These handlers are only added the first time the module
+                // is loaded because we don't want to have a new listener
+                // added each time the "init" function is called otherwise we'll
+                // end up with lots of stale handlers.
+                document.addEventListener('dragstart', dragstartHandler, false);
+                document.addEventListener('dragover', dragoverHandler, false);
+                document.addEventListener('dragleave', dragleaveHandler, false);
+                document.addEventListener('drop', dropHandler, false);
+                document.addEventListener('dragend', removeDropZoneIndicator, false);
+                registered = true;
+            }
+
+            // Update the module variable to operate on the given
+            // root element.
+            root = $(rootElement);
+
+            // If we're currently dragging then add the indicators.
+            if (DataStore.hasEventId()) {
+                addDropZoneIndicator();
+            }
+        },
+    };
+});
similarity index 64%
rename from calendar/amd/src/drag_drop.js
rename to calendar/amd/src/month_view_drag_drop.js
index de085f0..05b3733 100644 (file)
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
 /**
- * A javascript module to handle calendar drag and drop. This module
- * unfortunately requires some state to be maintained because of the
- * limitations of the HTML5 drag and drop API which means it can't
- * be used multiple times with the current implementation.
+ * A javascript module to handle calendar drag and drop in the calendar
+ * month view.
  *
- * @module     core_calendar/drag_drop
- * @class      drag_drop
+ * @module     core_calendar/month_view_drag_drop
+ * @class      month_view_drag_drop
  * @package    core_calendar
  * @copyright  2017 Ryan Wyllie <ryan@moodle.com>
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 define([
             'jquery',
-            'core_calendar/events'
+            'core_calendar/events',
+            'core_calendar/drag_drop_data_store'
         ],
         function(
             $,
-            CalendarEvents
+            CalendarEvents,
+            DataStore
         ) {
 
     var SELECTORS = {
         ROOT: "[data-region='calendar']",
-        DRAGGABLE: '[draggable="true"]',
-        DROP_ZONE: '[data-drop-zone="true"]',
+        DRAGGABLE: '[draggable="true"][data-region="event-item"]',
+        DROP_ZONE: '[data-drop-zone="month-view-day"]',
         WEEK: '[data-region="month-view-week"]',
     };
-    var HOVER_CLASS = 'bg-primary';
+    var HOVER_CLASS = 'bg-primary text-white';
+    /* @var {bool} registered If the event listeners have been added */
+    var registered = false;
 
-    // Unfortunately we are required to maintain some module
-    // level state due to the limitations of the HTML5 drag
-    // and drop API. Specifically the inability to pass data
-    // between the dragstate and dragover events handlers
-    // using the DataTransfer object in the event.
-
-    /** @var int eventId The event id being moved. */
-    var eventId = null;
-    /** @var int duration The number of days the event spans */
-    var duration = null;
+    /**
+     * Get the correct drop zone element from the given javascript
+     * event.
+     *
+     * @param {event} e The javascript event
+     * @return {object|null}
+     */
+    var getDropZoneFromEvent = function(e) {
+        var dropZone = $(e.target).closest(SELECTORS.DROP_ZONE);
+        return (dropZone.length) ? dropZone : null;
+    };
 
     /**
      * Update the hover state for the event in the calendar to reflect
@@ -70,11 +73,10 @@ define([
      * @param {bool} hovered If the target is hovered or not
      * @param {int} count How many days to highlight (default to duration)
      */
-    var updateHoverState = function(target, hovered, count) {
-        var dropZone = $(target).closest(SELECTORS.DROP_ZONE);
+    var updateHoverState = function(dropZone, hovered, count) {
         if (typeof count === 'undefined') {
             // This is how many days we need to highlight.
-            count = duration;
+            count = DataStore.getDurationDays();
         }
 
         if (hovered) {
@@ -115,16 +117,20 @@ define([
      * @param {event} e The dragstart event
      */
     var dragstartHandler = function(e) {
-        var eventElement = $(e.target);
+        var eventElement = $(e.target).closest(SELECTORS.DRAGGABLE);
 
-        if (!eventElement.is('[data-event-id]')) {
-            eventElement = eventElement.find('[data-event-id]');
+        if (!eventElement.length) {
+            return;
         }
 
-        eventId = eventElement.attr('data-event-id');
+        eventElement = eventElement.find('[data-event-id]');
 
+        var eventId = eventElement.attr('data-event-id');
         var eventsSelector = SELECTORS.ROOT + ' [data-event-id="' + eventId + '"]';
-        duration = $(eventsSelector).length;
+        var duration = $(eventsSelector).length;
+
+        DataStore.setEventId(eventId);
+        DataStore.setDurationDays(duration);
 
         e.dataTransfer.effectAllowed = "move";
         e.dataTransfer.dropEffect = "move";
@@ -145,7 +151,14 @@ define([
      */
     var dragoverHandler = function(e) {
         e.preventDefault();
-        updateHoverState(e.target, true);
+
+        var dropZone = getDropZoneFromEvent(e);
+
+        if (!dropZone) {
+            return;
+        }
+
+        updateHoverState(dropZone, true);
     };
 
     /**
@@ -158,8 +171,14 @@ define([
      * @param {event} e The dragstart event
      */
     var dragleaveHandler = function(e) {
+        var dropZone = getDropZoneFromEvent(e);
+
+        if (!dropZone) {
+            return;
+        }
+
+        updateHoverState(dropZone, false);
         e.preventDefault();
-        updateHoverState(e.target, false);
     };
 
     /**
@@ -174,35 +193,46 @@ define([
      * @param {event} e The dragstart event
      */
     var dropHandler = function(e) {
-        e.preventDefault();
+        var dropZone = getDropZoneFromEvent(e);
+
+        if (!dropZone) {
+            DataStore.clearAll();
+            return;
+        }
 
+        var eventId = DataStore.getEventId();
         var eventElementSelector = SELECTORS.ROOT + ' [data-event-id="' + eventId + '"]';
         var eventElement = $(eventElementSelector);
-        var origin = eventElement.closest(SELECTORS.DROP_ZONE);
+        var origin = null;
         var destination = $(e.target).closest(SELECTORS.DROP_ZONE);
 
-        updateHoverState(e.target, false);
-        $('body').trigger(CalendarEvents.moveEvent, [eventElement, origin, destination]);
+        if (eventElement.length) {
+            origin = eventElement.closest(SELECTORS.DROP_ZONE);
+        }
+
+        updateHoverState(dropZone, false);
+        $('body').trigger(CalendarEvents.moveEvent, [eventId, origin, destination]);
+        DataStore.clearAll();
+
+        e.preventDefault();
     };
 
     return {
         /**
          * Initialise the event handlers for the drag events.
-         *
-         * @param {object} root The root calendar element that containers the drag drop elements
          */
-        init: function(root) {
-            root = $(root);
-
-            root.find(SELECTORS.DRAGGABLE).each(function(index, element) {
-                element.addEventListener('dragstart', dragstartHandler, true);
-            });
-
-            root.find(SELECTORS.DROP_ZONE).each(function(index, element) {
-                element.addEventListener('dragover', dragoverHandler, true);
-                element.addEventListener('dragleave', dragleaveHandler, true);
-                element.addEventListener('drop', dropHandler, true);
-            });
+        init: function() {
+            if (!registered) {
+                // These handlers are only added the first time the module
+                // is loaded because we don't want to have a new listener
+                // added each time the "init" function is called otherwise we'll
+                // end up with lots of stale handlers.
+                document.addEventListener('dragstart', dragstartHandler, false);
+                document.addEventListener('dragover', dragoverHandler, false);
+                document.addEventListener('dragleave', dragleaveHandler, false);
+                document.addEventListener('drop', dropHandler, false);
+                registered = true;
+            }
         },
     };
 });
index eccb0a3..38e7971 100644 (file)
@@ -26,7 +26,7 @@ define(['jquery', 'core/templates', 'core/notification', 'core_calendar/reposito
 
         var SELECTORS = {
             ROOT: "[data-region='calendar']",
-            CALENDAR_NAV_LINK: "span.calendarwrapper .arrow_link",
+            CALENDAR_NAV_LINK: ".calendarwrapper .arrow_link",
             CALENDAR_MONTH_WRAPPER: ".calendarwrapper",
         };
 
index b088e71..3ad809b 100644 (file)
@@ -2175,6 +2175,7 @@ function calendar_get_link_previous($text, $linkbase, $d, $m, $y, $accesshide =
 
     $attrs = [
         'data-time' => calendar_get_timestamp($d, $m, $y, $time),
+        'data-drop-zone' => 'nav-link',
     ];
 
     return link_arrow_left($text, $href->out(false), $accesshide, 'previous', $attrs);
@@ -2202,6 +2203,7 @@ function calendar_get_link_next($text, $linkbase, $d, $m, $y, $accesshide = fals
 
     $attrs = [
         'data-time' => calendar_get_timestamp($d, $m, $y, $time),
+        'data-drop-zone' => 'nav-link',
     ];
 
     return link_arrow_right($text, $href->out(false), $accesshide, 'next', $attrs);
index 9d17086..ec8cf9d 100644 (file)
     {
     }
 }}
-<div id="month-detailed-{{uniqid}}" class="calendarwrapper" data-courseid="{{courseid}}" data-current-time="{{time}}">
+<div class="calendarwrapper" data-courseid="{{courseid}}" data-current-time="{{time}}">
     {{> core_calendar/month_header }}
     {{> core_calendar/month_navigation }}
-    <table class="calendarmonth calendartable card-deck m-b-0">
+    <table id="month-detailed-{{uniqid}}" class="calendarmonth calendartable card-deck m-b-0">
         <thead>
             <tr>
                 {{# daynames }}
@@ -58,7 +58,7 @@
                             }}{{#durationevents}} duration_{{.}}{{/durationevents}}{{!
                         }}"
                         data-day-timestamp="{{timestamp}}"
-                        data-drop-zone="true"
+                        data-drop-zone="month-view-day"
                         data-region="day"
                         data-new-event-timestamp="{{neweventtimestamp}}">
                         <div class="hidden-sm-down text-xs-center">
     </table>
 </div>
 {{#js}}
-require(['jquery', 'core_calendar/drag_drop'], function($, DragDrop) {
+require(['jquery', 'core_calendar/month_view_drag_drop'], function($, DragDrop) {
     var root = $('#month-detailed-{{uniqid}}');
     DragDrop.init(root);
 });
index d7d3509..f0f67dd 100644 (file)
     }
 }}
 {{#navigation}}
-<div class="controls" data-view="{{view}}">
+<div id="month-navigation-{{uniqid}}" class="controls" data-view="{{view}}">
     {{{navigation}}}
 </div>
 {{/navigation}}
+{{#js}}
+require(['jquery', 'core_calendar/month_navigation_drag_drop'], function($, DragDrop) {
+    var root = $('#month-navigation-{{uniqid}}');
+    DragDrop.init(root);
+});
+{{/js}}
index df6829e..2c04705 100644 (file)
@@ -48,6 +48,7 @@ $calendarEventUserColor: #dce7ec !default; // Pale blue.
 
         .previous {
             text-align: left;
+            border: 1px solid transparent;
         }
 
         .current {
@@ -57,6 +58,12 @@ $calendarEventUserColor: #dce7ec !default; // Pale blue.
 
         .next {
             text-align: right;
+            border: 1px solid transparent;
+        }
+
+        .drop-target {
+            box-sizing: border-box;
+            border: 1px dashed $brand-primary;
         }
     }
 
index 15d05d2..ccc168e 100644 (file)
     text-overflow: ellipsis;
     white-space: nowrap;
 }
+
+.text-white {
+    color: @white;
+}
+
+// Bg utilities.
+.bg {
+    // Important (red)
+    &-danger {
+        background-color: @errorText;
+    }
+    &-danger[href] {
+        background-color: darken(@errorText, 10%);
+    }
+    // Warnings (orange)
+    &-warning {
+        background-color: @orange;
+    }
+    &-warning[href] {
+        background-color: darken(@orange, 10%);
+    }
+    // Success (green)
+    &-success {
+        background-color: @successText;
+    }
+    &-success[href] {
+        background-color: darken(@successText, 10%);
+    }
+    // Info (turquoise)
+    &-info {
+        background-color: @infoText;
+    }
+    &-info[href] {
+        background-color: darken(@infoText, 10%);
+    }
+    // Primary (blue)
+    &-primary {
+        background-color: @blue;
+    }
+    &-primary[href] {
+        background-color: darken(@blue, 10%);
+    }
+}
index d89dd37..f74dcc8 100644 (file)
@@ -45,6 +45,8 @@
         }
         .previous {
             text-align: left;
+            border: 1px solid transparent;
+            box-sizing: border-box;
         }
         .current {
             text-align: center;
         }
         .next {
             text-align: right;
+            border: 1px solid transparent;
+            box-sizing: border-box;
+        }
+        .drop-target {
+            box-sizing: border-box;
+            border: 1px dashed @blue;
         }
     }
     .filters {
index 85e2608..7a4d53b 100644 (file)
@@ -2385,3 +2385,7 @@ h3.sectionname .inplaceeditable.inplaceeditingon .editinstructions {
 .clickable {
     cursor: pointer;
 }
+
+[data-drag-type="move"] {
+    cursor: move;
+}
index 70102d3..59154a6 100644 (file)
@@ -4777,6 +4777,9 @@ h3.sectionname .inplaceeditable.inplaceeditingon .editinstructions {
 .clickable {
   cursor: pointer;
 }
+[data-drag-type="move"] {
+  cursor: move;
+}
 /* admin.less */
 .formtable tbody th {
   font-weight: normal;
@@ -5576,6 +5579,8 @@ img.iconsmall {
 }
 .path-calendar .calendar-controls .previous {
   text-align: left;
+  border: 1px solid transparent;
+  box-sizing: border-box;
 }
 .path-calendar .calendar-controls .current {
   text-align: center;
@@ -5583,6 +5588,12 @@ img.iconsmall {
 }
 .path-calendar .calendar-controls .next {
   text-align: right;
+  border: 1px solid transparent;
+  box-sizing: border-box;
+}
+.path-calendar .calendar-controls .drop-target {
+  box-sizing: border-box;
+  border: 1px dashed #049cdb;
 }
 .path-calendar .filters table {
   border-collapse: separate;
@@ -21827,3 +21838,36 @@ ul.indented-list {
   text-overflow: ellipsis;
   white-space: nowrap;
 }
+.text-white {
+  color: #fff;
+}
+.bg-danger {
+  background-color: #b94a48;
+}
+.bg-danger[href] {
+  background-color: #953b39;
+}
+.bg-warning {
+  background-color: #f89406;
+}
+.bg-warning[href] {
+  background-color: #c67605;
+}
+.bg-success {
+  background-color: #468847;
+}
+.bg-success[href] {
+  background-color: #356635;
+}
+.bg-info {
+  background-color: #3a87ad;
+}
+.bg-info[href] {
+  background-color: #2d6987;
+}
+.bg-primary {
+  background-color: #049cdb;
+}
+.bg-primary[href] {
+  background-color: #0378a9;
+}