course/format/amd/src/local/courseeditor/dndsectionitem.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. * Course index section title draggable component.
  17. *
  18. * This component is used to control specific course section interactions like drag and drop
  19. * in both course index and course content.
  20. *
  21. * @module core_courseformat/local/courseeditor/dndsectionitem
  22. * @class core_courseformat/local/courseeditor/dndsectionitem
  23. * @copyright 2021 Ferran Recio <ferran@moodle.com>
  24. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  25. */
  26. import {BaseComponent, DragDrop} from 'core/reactive';
  27. export default class extends BaseComponent {
  28. /**
  29. * Initial state ready method.
  30. *
  31. * @param {number} sectionid the section id
  32. * @param {Object} state the initial state
  33. * @param {Element} fullregion the complete section region to mark as dragged
  34. */
  35. configDragDrop(sectionid, state, fullregion) {
  36. this.id = sectionid;
  37. if (this.section === undefined) {
  38. this.section = state.section.get(this.id);
  39. }
  40. if (this.course === undefined) {
  41. this.course = state.course;
  42. }
  43. // Prevent topic zero and delegated sections from being draggable.
  44. if (this.section.number > 0 && this.section.component === null) {
  45. this.getDraggableData = this._getDraggableData;
  46. }
  47. this.fullregion = fullregion;
  48. // Drag and drop is only available for components compatible course formats.
  49. if (this.reactive.isEditing && this.reactive.supportComponents) {
  50. // Init the dropzone.
  51. this.dragdrop = new DragDrop(this);
  52. // Save dropzone classes.
  53. this.classes = this.dragdrop.getClasses();
  54. }
  55. }
  56. /**
  57. * Remove all subcomponents dependencies.
  58. */
  59. destroy() {
  60. if (this.dragdrop !== undefined) {
  61. this.dragdrop.unregister();
  62. }
  63. }
  64. /**
  65. * Enable or disable the draggable property.
  66. *
  67. * @param {bool} value the new draggable value
  68. */
  69. setDraggable(value) {
  70. if (this.getDraggableData) {
  71. this.dragdrop?.setDraggable(value);
  72. }
  73. }
  74. // Drag and drop methods.
  75. /**
  76. * The element drop start hook.
  77. *
  78. * @param {Object} dropdata the dropdata
  79. */
  80. dragStart(dropdata) {
  81. this.reactive.dispatch('sectionDrag', [dropdata.id], true);
  82. }
  83. /**
  84. * The element end start hook.
  85. *
  86. * @param {Object} dropdata the dropdata
  87. */
  88. dragEnd(dropdata) {
  89. this.reactive.dispatch('sectionDrag', [dropdata.id], false);
  90. }
  91. /**
  92. * Get the draggable data of this component.
  93. *
  94. * @returns {Object} exported course module drop data
  95. */
  96. _getDraggableData() {
  97. const exporter = this.reactive.getExporter();
  98. return exporter.sectionDraggableData(this.reactive.state, this.id);
  99. }
  100. /**
  101. * Validate if the drop data can be dropped over the component.
  102. *
  103. * @param {Object} dropdata the exported drop data.
  104. * @returns {boolean}
  105. */
  106. validateDropData(dropdata) {
  107. // Course module validation.
  108. if (dropdata?.type === 'cm') {
  109. // Prevent content loops with subsections.
  110. if (this.section?.component && dropdata?.hasdelegatedsection === true) {
  111. return false;
  112. }
  113. // The first section element is already there so we can ignore it.
  114. const firstcmid = this.section?.cmlist[0];
  115. return dropdata.id !== firstcmid;
  116. }
  117. return false;
  118. }
  119. /**
  120. * Display the component dropzone.
  121. */
  122. showDropZone() {
  123. this.element.classList.add(this.classes.DROPZONE);
  124. }
  125. /**
  126. * Hide the component dropzone.
  127. */
  128. hideDropZone() {
  129. this.element.classList.remove(this.classes.DROPZONE);
  130. }
  131. /**
  132. * Drop event handler.
  133. *
  134. * @param {Object} dropdata the accepted drop data
  135. * @param {Event} event the drop event
  136. */
  137. drop(dropdata, event) {
  138. // Call the move mutation.
  139. if (dropdata.type == 'cm') {
  140. const mutation = (event.altKey) ? 'cmDuplicate' : 'cmMove';
  141. this.reactive.dispatch(mutation, [dropdata.id], this.id, this.section?.cmlist[0]);
  142. }
  143. }
  144. }