calendar/amd/src/month_navigation_drag_drop.js

  1. // This file is part of Moodle - http://moodle.org/
  2. //
  3. // Moodle is free software: you can redistribute it and/or modify
  4. // it under the terms of the GNU General Public License as published by
  5. // the Free Software Foundation, either version 3 of the License, or
  6. // (at your option) any later version.
  7. //
  8. // Moodle is distributed in the hope that it will be useful,
  9. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. // GNU General Public License for more details.
  12. //
  13. // You should have received a copy of the GNU General Public License
  14. // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
  15. /**
  16. * A javascript module to handle calendar drag and drop in the calendar
  17. * month view navigation.
  18. *
  19. * This code is run each time the calendar month view is re-rendered. We
  20. * only register the event handlers once per page load so that the in place
  21. * DOM updates that happen on month change don't continue to register handlers.
  22. *
  23. * @module core_calendar/month_navigation_drag_drop
  24. * @copyright 2017 Ryan Wyllie <ryan@moodle.com>
  25. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  26. */
  27. define([
  28. 'jquery',
  29. 'core_calendar/drag_drop_data_store',
  30. ],
  31. function(
  32. $,
  33. DataStore
  34. ) {
  35. var SELECTORS = {
  36. DRAGGABLE: '[draggable="true"][data-region="event-item"]',
  37. DROP_ZONE: '[data-drop-zone="nav-link"]',
  38. };
  39. var HOVER_CLASS = 'bg-primary text-white';
  40. var TARGET_CLASS = 'drop-target';
  41. var HOVER_TIME = 1000; // 1 second hover to change month.
  42. // We store some static variables at the module level because this
  43. // module is called each time the calendar month view is reloaded but
  44. // we want some actions to only occur ones.
  45. /* @var {bool} registered If the event listeners have been added */
  46. var registered = false;
  47. /* @var {int} hoverTimer The timeout id of any timeout waiting for hover */
  48. var hoverTimer = null;
  49. /* @var {object} root The root nav element we're operating on */
  50. var root = null;
  51. /**
  52. * Add or remove the appropriate styling to indicate whether
  53. * the drop target is being hovered over.
  54. *
  55. * @param {object} target The target drop zone element
  56. * @param {bool} hovered If the element is hovered over ot not
  57. */
  58. var updateHoverState = function(target, hovered) {
  59. if (hovered) {
  60. target.addClass(HOVER_CLASS);
  61. } else {
  62. target.removeClass(HOVER_CLASS);
  63. }
  64. };
  65. /**
  66. * Add some styling to the UI to indicate that the nav links
  67. * are an acceptable drop target.
  68. */
  69. var addDropZoneIndicator = function() {
  70. root.find(SELECTORS.DROP_ZONE).addClass(TARGET_CLASS);
  71. };
  72. /**
  73. * Remove the styling from the nav links.
  74. */
  75. var removeDropZoneIndicator = function() {
  76. root.find(SELECTORS.DROP_ZONE).removeClass(TARGET_CLASS);
  77. };
  78. /**
  79. * Get the drop zone target from the event, if one is found.
  80. *
  81. * @param {event} e Javascript event
  82. * @return {object|null}
  83. */
  84. var getTargetFromEvent = function(e) {
  85. var target = $(e.target).closest(SELECTORS.DROP_ZONE);
  86. return (target.length) ? target : null;
  87. };
  88. /**
  89. * This will add a visual indicator to the calendar UI to
  90. * indicate which nav link is a valid drop zone.
  91. *
  92. * @param {Event} e
  93. */
  94. var dragstartHandler = function(e) {
  95. // Make sure the drag event is for a calendar event.
  96. var eventElement = $(e.target).closest(SELECTORS.DRAGGABLE);
  97. if (eventElement.length) {
  98. addDropZoneIndicator();
  99. }
  100. };
  101. /**
  102. * Update the hover state of the target nav element when
  103. * the user is dragging an event over it.
  104. *
  105. * This will add a visual indicator to the calendar UI to
  106. * indicate which nav link is being hovered.
  107. *
  108. * @param {event} e The dragover event
  109. */
  110. var dragoverHandler = function(e) {
  111. // Ignore dragging of non calendar events.
  112. if (!DataStore.hasEventId()) {
  113. return;
  114. }
  115. e.preventDefault();
  116. var target = getTargetFromEvent(e);
  117. if (!target) {
  118. return;
  119. }
  120. // If we're not draggin a calendar event then
  121. // ignore it.
  122. if (!DataStore.hasEventId()) {
  123. return;
  124. }
  125. if (!hoverTimer) {
  126. hoverTimer = setTimeout(function() {
  127. target.click();
  128. hoverTimer = null;
  129. }, HOVER_TIME);
  130. }
  131. updateHoverState(target, true);
  132. removeDropZoneIndicator();
  133. };
  134. /**
  135. * Update the hover state of the target nav element that was
  136. * previously dragged over but has is no longer a drag target.
  137. *
  138. * This will remove the visual indicator from the calendar UI
  139. * that was added by the dragoverHandler.
  140. *
  141. * @param {event} e The dragstart event
  142. */
  143. var dragleaveHandler = function(e) {
  144. // Ignore dragging of non calendar events.
  145. if (!DataStore.hasEventId()) {
  146. return;
  147. }
  148. var target = getTargetFromEvent(e);
  149. if (!target) {
  150. return;
  151. }
  152. if (hoverTimer) {
  153. clearTimeout(hoverTimer);
  154. hoverTimer = null;
  155. }
  156. updateHoverState(target, false);
  157. addDropZoneIndicator();
  158. e.preventDefault();
  159. };
  160. /**
  161. * Remove the visual indicator from the calendar UI that was
  162. * added by the dragoverHandler.
  163. *
  164. * @param {event} e The drop event
  165. */
  166. var dropHandler = function(e) {
  167. // Ignore dragging of non calendar events.
  168. if (!DataStore.hasEventId()) {
  169. return;
  170. }
  171. removeDropZoneIndicator();
  172. var target = getTargetFromEvent(e);
  173. if (!target) {
  174. return;
  175. }
  176. updateHoverState(target, false);
  177. e.preventDefault();
  178. };
  179. return {
  180. /**
  181. * Initialise the event handlers for the drag events.
  182. *
  183. * @param {object} rootElement The element containing calendar nav links
  184. */
  185. init: function(rootElement) {
  186. // Only register the handlers once on the first load.
  187. if (!registered) {
  188. // These handlers are only added the first time the module
  189. // is loaded because we don't want to have a new listener
  190. // added each time the "init" function is called otherwise we'll
  191. // end up with lots of stale handlers.
  192. document.addEventListener('dragstart', dragstartHandler, false);
  193. document.addEventListener('dragover', dragoverHandler, false);
  194. document.addEventListener('dragleave', dragleaveHandler, false);
  195. document.addEventListener('drop', dropHandler, false);
  196. document.addEventListener('dragend', removeDropZoneIndicator, false);
  197. registered = true;
  198. }
  199. // Update the module variable to operate on the given
  200. // root element.
  201. root = $(rootElement);
  202. // If we're currently dragging then add the indicators.
  203. if (DataStore.hasEventId()) {
  204. addDropZoneIndicator();
  205. }
  206. },
  207. };
  208. });