calendar/amd/src/popover.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. * Javascript popover for the `core_calendar` subsystem.
  17. *
  18. * @module core_calendar/popover
  19. * @copyright 2021 Huong Nguyen <huongnv13@gmail.com>
  20. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  21. * @since 4.0
  22. */
  23. import Popover from 'theme_boost/bootstrap/popover';
  24. import * as CalendarSelectors from 'core_calendar/selectors';
  25. /**
  26. * Check if we are allowing to enable the popover or not.
  27. * @param {Element} dateContainer
  28. * @returns {boolean}
  29. */
  30. const isPopoverAvailable = (dateContainer) => {
  31. return window.getComputedStyle(dateContainer.querySelector(CalendarSelectors.elements.dateContent)).display === 'none';
  32. };
  33. const isPopoverConfigured = new Map();
  34. const showPopover = target => {
  35. const dateContainer = target.closest(CalendarSelectors.elements.dateContainer);
  36. if (!isPopoverConfigured.has(dateContainer)) {
  37. const config = {
  38. trigger: 'manual',
  39. placement: 'top',
  40. html: true,
  41. title: dateContainer.dataset.title,
  42. content: () => {
  43. const source = dateContainer.querySelector(CalendarSelectors.elements.dateContent);
  44. return source ? source.querySelector('.hidden').innerHTML : '';
  45. },
  46. 'animation': false,
  47. };
  48. new Popover(target, config);
  49. isPopoverConfigured.set(dateContainer, true);
  50. }
  51. if (isPopoverAvailable(dateContainer)) {
  52. Popover.getInstance(target).show();
  53. target.addEventListener('mouseleave', hidePopover);
  54. target.addEventListener('focusout', hidePopover);
  55. // Set up the hide function to the click event type.
  56. target.addEventListener('click', hidePopover);
  57. }
  58. };
  59. const hidePopover = e => {
  60. const target = e.target;
  61. const dateContainer = e.target.closest(CalendarSelectors.elements.dateContainer);
  62. if (!dateContainer) {
  63. return;
  64. }
  65. if (isPopoverConfigured.has(dateContainer)) {
  66. const isTargetActive = target.contains(document.activeElement);
  67. const isTargetHover = target.matches(':hover');
  68. // Checks if a target element is clicked or pressed.
  69. const isTargetClicked = document.activeElement.contains(target);
  70. let removeListener = true;
  71. if (!isTargetActive && !isTargetHover) {
  72. Popover.getOrCreateInstance(target).hide();
  73. } else if (isTargetClicked) {
  74. Popover.getOrCreateInstance(document.activeElement).hide();
  75. } else {
  76. removeListener = false;
  77. }
  78. if (removeListener) {
  79. target.removeEventListener('mouseleave', hidePopover);
  80. target.removeEventListener('focusout', hidePopover);
  81. target.removeEventListener('click', hidePopover);
  82. }
  83. }
  84. };
  85. /**
  86. * Register events for date container.
  87. */
  88. const registerEventListeners = () => {
  89. const showPopoverHandler = (e) => {
  90. const dayLink = e.target.closest(CalendarSelectors.links.dayLink);
  91. if (!dayLink) {
  92. return;
  93. }
  94. e.preventDefault();
  95. showPopover(dayLink);
  96. };
  97. document.addEventListener('mouseover', showPopoverHandler);
  98. document.addEventListener('focusin', showPopoverHandler);
  99. };
  100. let listenersRegistered = false;
  101. if (!listenersRegistered) {
  102. registerEventListeners();
  103. listenersRegistered = true;
  104. }