question/bank/managecategories/amd/src/newchild.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. * The newchild component.
  17. *
  18. * This is a drop target for moving a category to an as-yet-nonexistant child list under another category.
  19. *
  20. * @module qbank_managecategories/newchild
  21. * @class qbank_managecategories/newchild
  22. */
  23. import {BaseComponent, DragDrop} from 'core/reactive';
  24. import {categorymanager} from 'qbank_managecategories/categorymanager';
  25. import Tooltip from 'theme_boost/bootstrap/tooltip';
  26. export default class extends BaseComponent {
  27. create(descriptor) {
  28. this.name = descriptor.element.id;
  29. this.selectors = {
  30. NEW_CHILD: '.qbank_managecategories-newchild',
  31. CATEGORY_ID: id => `#category-${id}`
  32. };
  33. this.classes = {
  34. DROP_TARGET: 'qbank_managecategories-droptarget',
  35. };
  36. this.ids = {
  37. CATEGORY: id => `category-${id}`,
  38. };
  39. }
  40. stateReady() {
  41. this.dragdrop = new DragDrop(this);
  42. }
  43. destroy() {
  44. // The draggable element must be unregistered.
  45. if (this.dragdrop !== undefined) {
  46. this.dragdrop.unregister();
  47. this.dragdrop = undefined;
  48. }
  49. }
  50. /**
  51. * Static method to create a component instance form the mustache template.
  52. *
  53. * @param {string} target the DOM main element or its ID
  54. * @param {object} selectors optional css selector overrides
  55. * @return {Component}
  56. */
  57. static init(target, selectors) {
  58. const targetElement = document.querySelector(target);
  59. return new this({
  60. element: targetElement,
  61. selectors,
  62. reactive: categorymanager,
  63. });
  64. }
  65. /**
  66. * Cannot drop a category as a new child of its own descendant.
  67. *
  68. * @param {Object} dropData
  69. * @return {boolean}
  70. */
  71. validateDropData(dropData) {
  72. if (this.getElement().closest(this.selectors.CATEGORY_ID(dropData.id))) {
  73. return false;
  74. }
  75. return true;
  76. }
  77. showDropZone(dropData, event) {
  78. const dropTarget = event.target.closest(this.selectors.NEW_CHILD);
  79. dropTarget.classList.add(this.classes.DROP_TARGET);
  80. Tooltip.getOrCreateInstance(dropTarget).show();
  81. }
  82. hideDropZone(dropData, event) {
  83. const dropTarget = event.target.closest(this.selectors.NEW_CHILD);
  84. dropTarget.classList.remove(this.classes.DROP_TARGET);
  85. Tooltip.getOrCreateInstance(dropTarget).hide();
  86. }
  87. drop(dropData, event) {
  88. const dropTarget = event.target.closest(this.selectors.NEW_CHILD);
  89. if (!dropTarget) {
  90. return;
  91. }
  92. const source = document.getElementById(this.ids.CATEGORY(dropData.id));
  93. if (!source) {
  94. return;
  95. }
  96. const targetParentId = dropTarget.dataset.parent;
  97. // Insert the category as the first child of the new parent.
  98. categorymanager.moveCategory(dropData.id, targetParentId);
  99. }
  100. /**
  101. * Watch for categories moving to a new parent.
  102. *
  103. * @return {Array} A list of watchers.
  104. */
  105. getWatchers() {
  106. return [
  107. // Watch for any category having its parent changed.
  108. {watch: `categories.parent:updated`, handler: this.checkNewChild},
  109. ];
  110. }
  111. /**
  112. * If an element now has this category as the parent, remove this new child target.
  113. *
  114. * @param {Object} args
  115. * @param {Element} args.element
  116. */
  117. checkNewChild({element}) {
  118. if (element.parent === parseInt(this.element.dataset.parent)) {
  119. this.remove();
  120. }
  121. }
  122. }