diff --git a/lib/visual.inc.php b/lib/visual.inc.php index 98d2b21eef2401ac50f13fcda7a39819c5f944eb..f61583b8399826c9d56bd637e44f3ecc2c16ba97 100644 --- a/lib/visual.inc.php +++ b/lib/visual.inc.php @@ -643,7 +643,12 @@ function tooltipIcon($text, $important = false, $html = false): string // render tooltip $template = $GLOBALS['template_factory']->open('shared/tooltip'); - return $template->render(compact('text', 'important', 'html')); + return $template->render([ + 'text' => $text, + 'important' => $important, + 'html' => $html, + 'tooltip_id' => md5($text) + ]); } /** @@ -655,9 +660,13 @@ function tooltipIcon($text, $important = false, $html = false): string function tooltipHtmlIcon($text, $important = false) { // render tooltip - $html = true; $template = $GLOBALS['template_factory']->open('shared/tooltip'); - return $template->render(compact('text', 'important', 'html')); + return $template->render([ + 'text' => $text, + 'important' => $important, + 'html' => true, + 'tooltip_id' => md5($text) + ]); } /** diff --git a/resources/assets/javascripts/bootstrap/tooltip.js b/resources/assets/javascripts/bootstrap/tooltip.js deleted file mode 100644 index c84042b82534486b0700efea51f1fad77c4396bb..0000000000000000000000000000000000000000 --- a/resources/assets/javascripts/bootstrap/tooltip.js +++ /dev/null @@ -1,67 +0,0 @@ -// Attach global hover handler for tooltips. -// Applies to all elements having a "data-tooltip" attribute. -// Tooltip may be provided in the data-attribute itself or by -// defining a title attribute. The latter is prefered due to -// the obvious accessibility issues. - -var timeout = null; - -STUDIP.Tooltip.threshold = 6; - -$(document).on('mouseenter mouseleave focusin focusout', '[data-tooltip],.tooltip:has(.tooltip-content)', function(event) { - let data = $(this).data(); - - const visible = event.type === 'mouseenter' || event.type === 'focusin'; - const offset = $(this).offset(); - const x = offset.left + $(this).outerWidth(true) / 2; - const y = offset.top; - const delay = data.tooltipDelay ?? 300; - - let content; - let tooltip; - - if (!data.tooltipObject) { - // If tooltip has not yet been created (first hover), obtain it's - // contents and create the actual tooltip object. - if (!data.tooltip || !$.isPlainObject(data.tooltip)) { - content = $('<div/>').text(data.tooltip || $(this).attr('title')).html(); - } else if (data.tooltip.html !== undefined) { - content = data.tooltip.html; - } else if (data.tooltip.text !== undefined) { - content = data.tooltip.text; - } else { - throw "Invalid content for tooltip via data"; - } - if (!content) { - content = $(this).find('.tooltip-content').remove().html(); - } - $(this).attr('title', null); - $(this).attr('data-tooltip', content); - - tooltip = new STUDIP.Tooltip(x, y, content); - - data.tooltipObject = tooltip; - $(this).attr('aria-describedby', tooltip.id); - - $(this).on('remove', function() { - tooltip.remove(); - }); - } else if (visible) { - // If tooltip has already been created, update it's position. - // This is neccessary if the surrounding content is scrollable AND has - // been scrolled. Otherwise the tooltip would appear at it's previous - // and now wrong location. - data.tooltipObject.position(x, y); - } - - if (visible) { - $('.studip-tooltip').not(data.tooltipObject).hide(); - data.tooltipObject.show(); - } else { - timeout = setTimeout(() => data.tooltipObject.hide(), delay); - } -}).on('mouseenter focusin', '.studip-tooltip', () => { - clearTimeout(timeout); -}).on('mouseleave focusout', '.studip-tooltip', function() { - $(this).hide(); -}); diff --git a/resources/assets/javascripts/entry-base.js b/resources/assets/javascripts/entry-base.js index 70ef84e571f30a6a2266b64a4668d672489b483c..9739872f7a32e40ecfa74339c84f56b687baeefd 100644 --- a/resources/assets/javascripts/entry-base.js +++ b/resources/assets/javascripts/entry-base.js @@ -56,7 +56,6 @@ import "./bootstrap/article.js" import "./bootstrap/copyable_links.js" import "./bootstrap/selection.js" import "./bootstrap/data_secure.js" -import "./bootstrap/tooltip.js" import "./bootstrap/lightbox.js" import "./bootstrap/application.js" import "./bootstrap/global_search.js" diff --git a/resources/assets/javascripts/init.js b/resources/assets/javascripts/init.js index 27039d6fd3a964d87ab85a03bd28cda6a152365e..5c62580b90851a92728c2c7834cd9553be4185a5 100644 --- a/resources/assets/javascripts/init.js +++ b/resources/assets/javascripts/init.js @@ -76,7 +76,6 @@ import study_area_selection from './lib/study_area_selection.js'; import Table from './lib/table.js'; import TableOfContents from './lib/table-of-contents.js'; import Toolbar from './lib/toolbar.js'; -import Tooltip from './lib/tooltip.js'; import Tour from './lib/tour.js'; import * as Gettext from './lib/gettext.js'; import UserFilter from './lib/user_filter.js'; @@ -163,7 +162,6 @@ window.STUDIP = _.assign(window.STUDIP || {}, { Table, TableOfContents, Toolbar, - Tooltip, Tour, URLHelper, UserFilter, diff --git a/resources/assets/javascripts/lib/tooltip.js b/resources/assets/javascripts/lib/tooltip.js deleted file mode 100644 index 2cdac27be88aaa69b6fa0cdba5cfae2af8e9c826..0000000000000000000000000000000000000000 --- a/resources/assets/javascripts/lib/tooltip.js +++ /dev/null @@ -1,227 +0,0 @@ -import CSS from './css.js'; - -/** - * Tooltip library for Stud.IP - * - * @author Jan-Hendrik Willms <tleilax+studip@gmail.com> - * @copyright Stud.IP Core Group 2014 - * @license GPL2 or any later version - * @since Stud.IP 3.1 - */ - -let count = 0; -let threshold = 0; - -class Tooltip { - static get count() { - return count; - } - - static set count(value) { - count = value; - } - - // Threshold used for "edge detection" (imagine a padding along the edges) - static get threshold() { - return threshold; - } - - static set threshold(value) { - threshold = value; - } - - /** - * Returns a new unique id of a tooltip. - * - * @return {string} Unique id - * @static - */ - static getId() { - const id = `studip-tooltip-${Tooltip.count}`; - Tooltip.count += 1; - return id; - } - - /** - * Constructs a new tooltip at given location with given content. - * The applied css class may be changed by the fourth parameter. - * - * @class - * @classdesc Stud.IP tooltips provide an improved layout and handling - * of contents (including html) than the browser's default - * tooltip through title attribute would - * - * @param {int} x - Horizontal position of the tooltip - * @param {int} y - Vertical position of the tooltip - * @param {string} content - Content of the tooltip (may be html) - * @param {string} css_class - Optional name of the applied css class / - * defaults to 'studip-tooltip' - */ - constructor(x, y, content, css_class) { - // Obtain unique id of the tooltip - this.id = Tooltip.getId(); - - // Create dom element of the tooltip, apply id and class and attach - // to dom - this.element = $('<div>'); - this.element.addClass(css_class || 'studip-tooltip'); - this.element.attr('id', this.id); - this.element.attr('role', 'tooltip'); - this.element.appendTo('body'); - - // Set position and content and paint the tooltip - this.position(x, y); - this.update(content); - this.paint(); - } - - /** - * Translates the arrow(s) under a tooltip using css3 translate - * transforms. This is needed at the edges of the screen. - * This implies that a current browser is used. The translation could - * also be achieved by adjusting margins but that way we would need - * to hardcode values into this function since it's a struggle to - * obtain the neccessary values from the CSS pseudo selectors in JS. - * - * Internal, css rules are dynamically created and applied to the current - * document by using the methods provided in the file studip-css.js. - * - * @param {int} x - Horizontal offset - * @param {int} y - Vertical offset - */ - translateArrows(x, y, left_arrow = false) { - CSS.removeRule(`#${this.id}::before`); - CSS.removeRule(`#${this.id}::after`); - - if (x !== 0 || y !== 0) { - let before_rule = { - transform: `translate(${x}px, ${y}px);` - }; - if (left_arrow) { - before_rule.transform = `translate(${x}px, ${y}px) rotate(90deg);`; - } - let after_rule = before_rule; - if (left_arrow) { - after_rule['border-width'] = '9px'; - } - CSS.addRule(`#${this.id}::before`, before_rule, ['-ms-', '-webkit-']); - CSS.addRule(`#${this.id}::after`, after_rule, ['-ms-', '-webkit-']); - } - } - - /** - * Updates the position of the tooltip. - * - * @param {int} x - Horizontal position of the tooltip - * @param {int} y - Vertical position of the tooltip - */ - position(x, y) { - this.x = x; - this.y = y; - } - - /** - * Updates the contents of the tooltip. - * - * @param {string} content - Content of the tooltip (may be html) - */ - update(content) { - this.element.html(content); - } - - /** - * "Paints" the tooltip. This method actually computes the dimensions of - * the tooltips, checks for screen edges and calculates the actual offset - * in the current document. - * This method is neccessary due to the fact that position and content - * can be changed apart from each other. - * Thus: Don't forget to repaint after adjusting any of the two. - */ - paint() { - const width = this.element.outerWidth(true); - const height = this.element.outerHeight(true); - const maxWidth = $(document).width(); - const maxHeight = $(document).height(); - let x = this.x - width / 2; - let y = this.y - height; - //The arrow offset is the offset from the bottom right corner of - //the tooltip "frame". - let arrow_offset_x = 0; - let arrow_offset_y = 0; - let left_arrow = false; - - if (y < 0) { - y = 0; - x = this.x + 20; - //Put the arrow on the left side and move the tooltip, - //if there is still enough place left on the right. - left_arrow = true; - arrow_offset_y = -height + this.y + 10; - if (arrow_offset_y > -20) { - y+= arrow_offset_y + 20; - arrow_offset_y = -20; - } - arrow_offset_x = -width / 2 - 8; - } else if (y + height > maxHeight) { - y = maxHeight - height; - } - - if (x < 0) { - arrow_offset_x = 0; - x = 0; - } else if (x + width > maxWidth) { - arrow_offset_x = x + width - maxWidth; - x = maxWidth - width; - } - this.translateArrows(arrow_offset_x, arrow_offset_y, left_arrow); - - this.element.css({ - left: x, - top: y - }); - } - - /** - * Toggles the visibility of the tooltip. If no state is provided, - * the tooltip will be hidden if visible and vice versa. Pretty straight - * forward and no surprises here. - * This method implicitely calls paint before a tooltip is shown (in case - * it was forgotten). - * - * @param {bool} visible - Optional visibility parameter to set the - * tooltip to a certain state - */ - toggle(visible) { - if (visible) { - this.paint(); - } - this.element.toggle(visible); - } - - /** - * Reveals the tooltip. - * - * @see Tooltip.toggle - */ - show() { - this.toggle(true); - } - - /** - * Hides the tooltip. - * - * @see Tooltip.toggle - */ - hide() { - this.toggle(false); - } - - /** - * Removes the tooltip - */ - remove() { - this.element.remove(); - } -} - -export default Tooltip; diff --git a/resources/assets/stylesheets/scss/tooltip.scss b/resources/assets/stylesheets/scss/tooltip.scss index a2f07e32469f7123b701e54f0fb0f75fe3b29013..dded92c804d6a8a7d313eea8fd51b36dca0e137f 100644 --- a/resources/assets/stylesheets/scss/tooltip.scss +++ b/resources/assets/stylesheets/scss/tooltip.scss @@ -7,7 +7,7 @@ box-shadow: 0 1px 0 fade-out(#fff, 0.5) inset; font-size: $font-size-base; margin-bottom: 8px; - max-width: 230px; + max-width: $grid-element-width; padding: 10px; position: absolute; text-align: left; @@ -38,11 +38,13 @@ @extend %tooltip; display: none; } - &:hover .tooltip-content { + + &:hover .tooltip-content, + &:focus .tooltip-content { bottom: 100%; display: inline-block; left: 50%; - margin-left: -129px; - width: 230px; + margin-left: - calc($grid-element-width / 2) - 10px; + width: $grid-element-width; } } diff --git a/templates/shared/tooltip.php b/templates/shared/tooltip.php index a97c73f275919a6d8639c8d074f8582a7d6673a3..8dbcd382421175fd5bd6b69a1e7a3690144d90f8 100644 --- a/templates/shared/tooltip.php +++ b/templates/shared/tooltip.php @@ -1,5 +1,4 @@ -<span class="tooltip tooltip-icon <? if ($important) echo 'tooltip-important'; ?>" data-tooltip <? if (!$html) printf('title="%s"', htmlReady($text)) ?> tabindex="0"> -<? if ($html): ?> - <span class="tooltip-content"><?= $text ?></span> -<? endif; ?> +<span class="tooltip tooltip-icon <? if ($important) echo 'tooltip-important'; ?>" + tabindex="0" aria-label="<?= $html ? kill_format($text) : htmlReady($text) ?>"> + <span class="tooltip-content"><?= $html ? $text : htmlReady($text) ?></span> </span>