Defect #42517 ยป 0001-Replace-jQuery-UI-tooltip-to-vanilla-js-tooltip.patch
| app/assets/javascripts/application.js | ||
|---|---|---|
| 1160 | 1160 |
}); |
| 1161 | 1161 |
} |
| 1162 | 1162 | |
| 1163 |
$(function () {
|
|
| 1164 |
$("[title]:not(.no-tooltip)").tooltip({
|
|
| 1165 |
show: {
|
|
| 1166 |
delay: 400 |
|
| 1167 |
}, |
|
| 1168 |
position: {
|
|
| 1169 |
my: "center bottom-5", |
|
| 1170 |
at: "center top" |
|
| 1171 |
} |
|
| 1172 |
}); |
|
| 1173 |
}); |
|
| 1174 | ||
| 1175 | 1163 |
function inlineAutoComplete(element) {
|
| 1176 | 1164 |
'use strict'; |
| 1177 | 1165 | |
| app/assets/stylesheets/application.css | ||
|---|---|---|
| 1348 | 1348 |
/***** Tooltips ******/ |
| 1349 | 1349 |
.tooltip{position:relative;z-index:24;}
|
| 1350 | 1350 |
.tooltip:hover{z-index:25;color:#000;}
|
| 1351 |
.tooltip span.tip{display: none; text-align:left;}
|
|
| 1352 | 1351 |
.tooltip span.tip a { color: #169 !important; }
|
| 1353 | 1352 | |
| 1354 | 1353 |
.tooltip span.tip img.gravatar {
|
| ... | ... | |
| 1356 | 1355 |
margin: 0; |
| 1357 | 1356 |
} |
| 1358 | 1357 | |
| 1359 |
div.tooltip:hover span.tip{
|
|
| 1358 |
div.tooltip span.tip{
|
|
| 1359 |
position:fixed; |
|
| 1360 |
text-align:left; |
|
| 1360 | 1361 |
display:block; |
| 1361 |
position:absolute;
|
|
| 1362 |
top:12px; width:270px;
|
|
| 1362 |
visibility:hidden;
|
|
| 1363 |
width:270px; |
|
| 1363 | 1364 |
border:1px solid #555; |
| 1364 | 1365 |
border-radius: 3px; |
| 1365 | 1366 |
background-color:#fff; |
| ... | ... | |
| 1367 | 1368 |
font-size: 0.75rem; |
| 1368 | 1369 |
color:#505050; |
| 1369 | 1370 |
box-shadow: 0 1px 2px rgba(0,0,0,0.05); |
| 1371 |
z-index:26; |
|
| 1370 | 1372 |
} |
| 1371 | 1373 | |
| 1372 |
table.cal div.tooltip:hover span.tip {
|
|
| 1373 |
top: 25px; |
|
| 1374 |
.action-tooltip {
|
|
| 1375 |
position: fixed; |
|
| 1376 |
display: inline-block!important; |
|
| 1377 |
font-weight: normal; |
|
| 1378 |
background: #000; |
|
| 1379 |
color: #fff; |
|
| 1380 |
border-radius: 3px; |
|
| 1381 |
padding: 10px; |
|
| 1382 |
visibility: hidden; |
|
| 1383 |
opacity: 0; |
|
| 1384 |
transition: 0.3s ease-in; |
|
| 1385 |
font-size: 0.8rem; |
|
| 1386 |
border: 0; |
|
| 1387 |
box-shadow: none; |
|
| 1388 |
white-space: pre-wrap; |
|
| 1389 |
pointer-events: none; |
|
| 1374 | 1390 |
} |
| 1375 | 1391 | |
| 1376 | 1392 |
img.ui-datepicker-trigger {
|
| ... | ... | |
| 1777 | 1793 |
background: #EEEEEE; |
| 1778 | 1794 |
} |
| 1779 | 1795 | |
| 1780 |
/***** Tooltips *****/ |
|
| 1781 |
.ui-tooltip {
|
|
| 1782 |
background: #000; |
|
| 1783 |
color: #fff; |
|
| 1784 |
border-radius: 3px; |
|
| 1785 |
border: 0; |
|
| 1786 |
box-shadow: none; |
|
| 1787 |
white-space: pre-wrap; |
|
| 1788 |
pointer-events: none; |
|
| 1789 |
} |
|
| 1790 | ||
| 1791 | 1796 |
/***** SVG Icons *****/ |
| 1792 | 1797 |
.icon, .icon-only {
|
| 1793 | 1798 |
display: inline-flex; |
| app/javascript/controllers/issue_tooltip_controller.js | ||
|---|---|---|
| 1 |
/** |
|
| 2 |
* Redmine - project management software |
|
| 3 |
* Copyright (C) 2006- Jean-Philippe Lang |
|
| 4 |
* This code is released under the GNU General Public License. |
|
| 5 |
*/ |
|
| 6 | ||
| 7 |
import { Controller } from "@hotwired/stimulus"
|
|
| 8 |
import { Tooltip } from 'tooltip'
|
|
| 9 | ||
| 10 |
// Connects to data-controller="issue--tooltip" |
|
| 11 |
export default class extends Controller {
|
|
| 12 |
show(e) {
|
|
| 13 |
const tooltip = new Tooltip({
|
|
| 14 |
delay: 0, |
|
| 15 |
selector: '.tooltip', |
|
| 16 |
createHook: (element) => {
|
|
| 17 |
element.tooltipElement = element.querySelector('.tip');
|
|
| 18 |
}, |
|
| 19 |
positionHook: (element, tooltip) => {
|
|
| 20 |
const trect = tooltip.getBoundingClientRect(); |
|
| 21 |
const rect = element.getBoundingClientRect(); |
|
| 22 |
const gap = Math.min(12, rect.height / 2) |
|
| 23 |
return {
|
|
| 24 |
top: rect.top + gap, |
|
| 25 |
left: rect.left + gap, |
|
| 26 |
width: trect.width, |
|
| 27 |
height: trect.height |
|
| 28 |
} |
|
| 29 |
} |
|
| 30 |
}); |
|
| 31 |
tooltip.show(e) |
|
| 32 |
} |
|
| 33 |
} |
|
| app/javascript/main.js | ||
|---|---|---|
| 1 | 1 |
import "controllers" |
| 2 |
import {createTooltip} from 'tooltip';
|
|
| 3 | ||
| 4 |
document.addEventListener('mouseover', (e) => {
|
|
| 5 |
const tooltip = createTooltip() |
|
| 6 |
tooltip.show(e) |
|
| 7 |
}); |
|
| app/javascript/tooltip.js | ||
|---|---|---|
| 1 |
/** |
|
| 2 |
* Redmine - project management software |
|
| 3 |
* Copyright (C) 2006- Jean-Philippe Lang |
|
| 4 |
* This code is released under the GNU General Public License. |
|
| 5 |
*/ |
|
| 6 | ||
| 7 |
export function createTooltip() {
|
|
| 8 |
const tooltip = new Tooltip({
|
|
| 9 |
selector: '[title]:not(.no-tooltip)', |
|
| 10 |
createHook: (element) => {
|
|
| 11 |
const tooltip = document.createElement('span')
|
|
| 12 |
tooltip.textContent = element.getAttribute('title');
|
|
| 13 |
element.setAttribute('title', '');
|
|
| 14 |
tooltip.classList.add('action-tooltip');
|
|
| 15 |
element.insertAdjacentElement('afterbegin', tooltip);
|
|
| 16 |
element.tooltipElement = tooltip; |
|
| 17 |
}, |
|
| 18 |
positionHook: (element, tooltip) => {
|
|
| 19 |
const trect = tooltip.getBoundingClientRect(); |
|
| 20 |
const rect = element.getBoundingClientRect(); |
|
| 21 | ||
| 22 |
return {
|
|
| 23 |
top: rect.top - trect.height - 5, |
|
| 24 |
left: rect.left + rect.width / 2 - trect.width / 2, |
|
| 25 |
width: trect.width, |
|
| 26 |
height: trect.height |
|
| 27 |
} |
|
| 28 |
} |
|
| 29 |
}); |
|
| 30 |
return tooltip; |
|
| 31 |
} |
|
| 32 | ||
| 33 |
export class Tooltip {
|
|
| 34 |
constructor(options) {
|
|
| 35 |
this.options = Object.assign({
|
|
| 36 |
delay: 400, |
|
| 37 |
selector: undefined, |
|
| 38 |
}, options) |
|
| 39 |
this.delayedShow = null; |
|
| 40 |
this.delayedHide = null; |
|
| 41 |
} |
|
| 42 | ||
| 43 |
show(e) {
|
|
| 44 |
const target = e.target.closest(this.options.selector) |
|
| 45 |
if (target !== null) {
|
|
| 46 | ||
| 47 |
if (target.tooltipElement === undefined) {
|
|
| 48 |
this.options.createHook(target); |
|
| 49 |
target.addEventListener('mouseleave', (e) => this.hide(e));
|
|
| 50 |
} |
|
| 51 | ||
| 52 |
this.delayedShow = setTimeout(() => {
|
|
| 53 |
this.setPosition(target); |
|
| 54 |
}, this.options.delay); |
|
| 55 |
} |
|
| 56 |
} |
|
| 57 | ||
| 58 |
hide(e) {
|
|
| 59 |
if (this.delayedShow !== null) {
|
|
| 60 |
clearTimeout(this.delayedShow); |
|
| 61 |
} |
|
| 62 |
this.delayedHide = setTimeout(() => {
|
|
| 63 |
const tooltip = e.target.tooltipElement; |
|
| 64 |
tooltip.style.visibility = 'hidden'; |
|
| 65 |
tooltip.style.opacity = 0; |
|
| 66 |
}, this.options.delay); |
|
| 67 |
} |
|
| 68 | ||
| 69 |
setPosition(target) {
|
|
| 70 |
const tooltip = target.tooltipElement; |
|
| 71 |
const position = this.options.positionHook(target, tooltip); |
|
| 72 |
const cheight = document.documentElement.clientHeight; |
|
| 73 |
const cwidth = document.documentElement.clientWidth; |
|
| 74 | ||
| 75 |
if (position.top + position.height > cheight) {
|
|
| 76 |
position.top = cheight - position.height; |
|
| 77 |
} |
|
| 78 | ||
| 79 |
if (position.left + position.width > cwidth) {
|
|
| 80 |
position.left = cwidth - position.width; |
|
| 81 |
} |
|
| 82 | ||
| 83 |
if (position.top < 0) {
|
|
| 84 |
position.top = 0; |
|
| 85 |
} |
|
| 86 | ||
| 87 |
if (position.left < 0) {
|
|
| 88 |
position.left = 0; |
|
| 89 |
} |
|
| 90 | ||
| 91 |
tooltip.style.top = `${position.top}px`;
|
|
| 92 |
tooltip.style.left = `${position.left}px`;
|
|
| 93 |
tooltip.style.visibility = 'visible'; |
|
| 94 |
tooltip.style.opacity = 1; |
|
| 95 |
} |
|
| 96 |
} |
|
| app/views/common/_calendar.html.erb | ||
|---|---|---|
| 1 | 1 |
<%= form_tag({}, :data => {:cm_url => issues_context_menu_path}) do -%>
|
| 2 | 2 |
<%= hidden_field_tag 'back_url', url_for(:params => request.query_parameters), :id => nil %> |
| 3 |
<ul class="cal"> |
|
| 3 |
<ul class="cal" data-controller="issue_tooltip" data-action="mouseover->issue_tooltip#show">
|
|
| 4 | 4 |
<li scope="col" title="<%= l(:label_week) %>" class="calhead week-number"></li> |
| 5 | 5 |
<% 7.times do |i| %> |
| 6 | 6 |
<li scope="col" class="calhead"><%= day_name((calendar.first_wday + i) % 7) %></li> |
| app/views/gantts/show.html.erb | ||
|---|---|---|
| 208 | 208 |
</td> |
| 209 | 209 |
<% end %> |
| 210 | 210 |
<td> |
| 211 |
<div style="position:relative;height:<%= t_height + 24 %>px;overflow:auto;" id="gantt_area"> |
|
| 211 |
<div style="position:relative;height:<%= t_height + 24 %>px;overflow:auto;" id="gantt_area" data-controller="issue_tooltip" data-action="mouseover->issue_tooltip#show">
|
|
| 212 | 212 |
<% |
| 213 | 213 |
style = "" |
| 214 | 214 |
style += "width: #{g_width - 1}px;"
|
| config/importmap.rb | ||
|---|---|---|
| 1 | 1 |
# Pin npm packages by running ./bin/importmap |
| 2 | 2 | |
| 3 | 3 |
pin "main" |
| 4 |
pin "tooltip" |
|
| 4 | 5 |
pin "@hotwired/stimulus", to: "stimulus.min.js" |
| 5 | 6 |
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js" |
| 6 | 7 |
pin_all_from "app/javascript/controllers", under: "controllers" |