You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

JitsiPopover.js 8.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. /* global $ */
  2. /* eslint-disable no-unused-vars */
  3. import React, { Component } from 'react';
  4. import ReactDOM from 'react-dom';
  5. import { I18nextProvider } from 'react-i18next';
  6. import { i18next } from '../../../react/features/base/i18n';
  7. /* eslint-enable no-unused-vars */
  8. const positionConfigurations = {
  9. left: {
  10. // Align the popover's right side to the target element.
  11. my: 'right',
  12. // Align the popover to the left side of the target element.
  13. at: 'left',
  14. // Force the popover to fit within the viewport.
  15. collision: 'fit',
  16. /**
  17. * Callback invoked by jQuery UI tooltip.
  18. *
  19. * @param {Object} position - The top and bottom position the popover
  20. * element should be set at.
  21. * @param {Object} element. - Additional size and position information
  22. * about the popover element and target.
  23. * @param {Object} elements.element - Has position and size related data
  24. * for the popover element itself.
  25. * @param {Object} elements.target - Has position and size related data
  26. * for the target element the popover displays from.
  27. */
  28. using: function setPositionLeft(position, elements) {
  29. const { element, target } = elements;
  30. $('.jitsipopover').css({
  31. left: element.left,
  32. top: element.top,
  33. visibility: 'visible'
  34. });
  35. // Move additional padding to the right edge of the popover and
  36. // allow css to take care of width. The padding is used to maintain
  37. // a hover state between the target and the popover.
  38. $('.jitsipopover > .jitsipopover__menu-padding')
  39. .css({ left: element.width });
  40. // Find the distance from the top of the popover to the center of
  41. // the target and use that value to position the arrow to point to
  42. // it.
  43. const verticalCenterOfTarget = target.height / 2;
  44. const verticalDistanceFromTops = target.top - element.top;
  45. const verticalPositionOfTargetCenter
  46. = verticalDistanceFromTops + verticalCenterOfTarget;
  47. $('.jitsipopover > .arrow').css({
  48. left: element.width,
  49. top: verticalPositionOfTargetCenter
  50. });
  51. }
  52. },
  53. top: {
  54. my: "bottom",
  55. at: "top",
  56. collision: "fit",
  57. using: function setPositionTop(position, elements) {
  58. const { element, target } = elements;
  59. const calcLeft = target.left - element.left + target.width / 2;
  60. const paddingLeftPosition = calcLeft - 50;
  61. const $jistiPopover = $('.jitsipopover');
  62. $jistiPopover.css({
  63. left: element.left,
  64. top: element.top,
  65. visibility: 'visible'
  66. });
  67. $jistiPopover.find('.arrow').css({ left: calcLeft });
  68. $jistiPopover.find('.jitsipopover__menu-padding')
  69. .css({ left: paddingLeftPosition });
  70. $jistiPopover.find('.jitsipopover__menu-padding-top')
  71. .css({ left: paddingLeftPosition });
  72. }
  73. }
  74. };
  75. export default (function () {
  76. /**
  77. * The default options
  78. */
  79. const defaultOptions = {
  80. skin: 'white',
  81. content: '',
  82. hasArrow: true,
  83. onBeforePosition: undefined,
  84. position: 'top'
  85. };
  86. /**
  87. * Constructs new JitsiPopover and attaches it to the element
  88. * @param element jquery selector
  89. * @param options the options for the popover.
  90. * @constructor
  91. */
  92. function JitsiPopover(element, options)
  93. {
  94. this.options = Object.assign({}, defaultOptions, options);
  95. this.elementIsHovered = false;
  96. this.popoverIsHovered = false;
  97. this.popoverShown = false;
  98. element.data("jitsi_popover", this);
  99. this.element = element;
  100. this.template = this.getTemplate();
  101. var self = this;
  102. this.element.on("mouseenter", function () {
  103. self.elementIsHovered = true;
  104. self.show();
  105. }).on("mouseleave", function () {
  106. self.elementIsHovered = false;
  107. setTimeout(function () {
  108. self.hide();
  109. }, 10);
  110. });
  111. }
  112. /**
  113. * Returns template for popover
  114. */
  115. JitsiPopover.prototype.getTemplate = function () {
  116. const { hasArrow, position, skin } = this.options;
  117. let arrow = '';
  118. if (hasArrow) {
  119. arrow = '<div class="arrow"></div>';
  120. }
  121. return (
  122. `<div class="jitsipopover ${skin} ${position}">
  123. <div class="jitsipopover__menu-padding-top"></div>
  124. ${arrow}
  125. <div class="jitsipopover__content"></div>
  126. <div class="jitsipopover__menu-padding"></div>
  127. </div>`
  128. );
  129. };
  130. /**
  131. * Shows the popover
  132. */
  133. JitsiPopover.prototype.show = function () {
  134. if(!JitsiPopover.enabled)
  135. return;
  136. this.createPopover();
  137. this.popoverShown = true;
  138. };
  139. /**
  140. * Hides the popover if not hovered or popover is not shown.
  141. */
  142. JitsiPopover.prototype.hide = function () {
  143. if(!this.elementIsHovered && !this.popoverIsHovered &&
  144. this.popoverShown) {
  145. this.forceHide();
  146. }
  147. };
  148. /**
  149. * Hides the popover and clears the document elements added by popover.
  150. */
  151. JitsiPopover.prototype.forceHide = function () {
  152. this.remove();
  153. this.popoverShown = false;
  154. if(this.popoverIsHovered) { //the browser is not firing hover events
  155. //when the element was on hover if got removed.
  156. this.popoverIsHovered = false;
  157. this.onHoverPopover(this.popoverIsHovered);
  158. }
  159. };
  160. /**
  161. * Creates the popover html.
  162. */
  163. JitsiPopover.prototype.createPopover = function () {
  164. let $popover = $('.jitsipopover');
  165. if (!$popover.length) {
  166. $('body').append(this.template);
  167. $popover = $('.jitsipopover');
  168. $popover.on('mouseenter', () => {
  169. this.popoverIsHovered = true;
  170. if (typeof this.onHoverPopover === 'function') {
  171. this.onHoverPopover(this.popoverIsHovered);
  172. }
  173. });
  174. $popover.on('mouseleave', () => {
  175. this.popoverIsHovered = false;
  176. this.hide();
  177. if (typeof this.onHoverPopover === 'function') {
  178. this.onHoverPopover(this.popoverIsHovered);
  179. }
  180. });
  181. }
  182. const $popoverContent = $popover.find('.jitsipopover__content');
  183. /* jshint ignore:start */
  184. ReactDOM.render(
  185. <I18nextProvider i18n = { i18next }>
  186. { this.options.content }
  187. </I18nextProvider>,
  188. $popoverContent.get(0),
  189. () => {
  190. this.refreshPosition();
  191. });
  192. /* jshint ignore:end */
  193. };
  194. /**
  195. * Adds a hover listener to the popover.
  196. */
  197. JitsiPopover.prototype.addOnHoverPopover = function (listener) {
  198. this.onHoverPopover = listener;
  199. };
  200. /**
  201. * Refreshes the position of the popover.
  202. */
  203. JitsiPopover.prototype.refreshPosition = function () {
  204. const positionOptions = Object.assign(
  205. {},
  206. positionConfigurations[this.options.position],
  207. {
  208. of: this.element
  209. }
  210. );
  211. $(".jitsipopover").position(positionOptions);
  212. };
  213. /**
  214. * Updates the content of popover.
  215. * @param content new content
  216. */
  217. JitsiPopover.prototype.updateContent = function (content) {
  218. this.options.content = content;
  219. if (!this.popoverShown) {
  220. return;
  221. }
  222. this.createPopover();
  223. };
  224. /**
  225. * Unmounts any present child React Component and removes the popover itself
  226. * from the DOM.
  227. *
  228. * @returns {void}
  229. */
  230. JitsiPopover.prototype.remove = function () {
  231. const $popover = $('.jitsipopover');
  232. const $popoverContent = $popover.find('.jitsipopover__content');
  233. if ($popoverContent.length) {
  234. ReactDOM.unmountComponentAtNode($popoverContent.get(0));
  235. }
  236. $popover.off();
  237. $popover.remove();
  238. };
  239. JitsiPopover.enabled = true;
  240. return JitsiPopover;
  241. })();