fix: structure calendar/Gantt hover cards into title and field grid

- Regroup Redmine's "<strong>Label</strong>: value<br>" tooltip lines on
  first hover into a title block and a label/value grid (theme.js).
- Style the issue link as a kicker, the subject as title-small, and the
  labels as a muted column next to aligned values.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Andreas Reinhold / reini
2026-10-05 12:44:19 +02:00
co-authored by Claude Opus 5.5
parent 6c271ffffe
commit 906153014b
2 changed files with 69 additions and 0 deletions
+34
View File
@@ -133,6 +133,40 @@
}); });
}); });
// Calendar/Gantt hover cards: Redmine renders "<a>Tracker #id</a>: subject<br><br>" followed by
// "<strong>Label</strong>: value<br>" lines. Regroup them on first hover into a title and a
// label/value grid (nodes are moved, not re-parsed, so links and avatars stay intact).
function structureTip(tip) {
var lines = [[]];
Array.prototype.slice.call(tip.childNodes).forEach(function (node) {
if (node.nodeName === 'BR') lines.push([]);
else if (node.nodeType !== 3 || node.nodeValue.trim()) lines[lines.length - 1].push(node);
});
var title = document.createElement('span');
var fields = document.createElement('span');
title.className = 'nm-tip__title';
fields.className = 'nm-tip__fields';
lines.filter(function (line) { return line.length; }).forEach(function (line, i) {
var head = line[0];
var value = document.createElement('span');
line.slice(1).forEach(function (node) { value.appendChild(node); });
if (value.firstChild && value.firstChild.nodeType === 3) {
value.firstChild.nodeValue = value.firstChild.nodeValue.replace(/^\s*:\s*/, '');
}
var target = i > 0 && head.nodeName === 'STRONG' ? fields : title;
target.appendChild(head);
target.appendChild(value);
});
tip.replaceChildren(title, fields);
tip.classList.add('nm-tip');
}
document.addEventListener('mouseover', function (event) {
var tip = event.target.closest && event.target.closest('div.tooltip');
tip = tip && tip.querySelector(':scope > span.tip:not(.nm-tip)');
if (tip) structureTip(tip);
});
document.addEventListener('DOMContentLoaded', function () { document.addEventListener('DOMContentLoaded', function () {
var menu = document.querySelector('#account .dropdown-content .dropdown-divider'); var menu = document.querySelector('#account .dropdown-content .dropdown-divider');
if (menu) menu.insertAdjacentHTML('afterend', pickerHtml() + '<div class="dropdown-divider"></div>'); if (menu) menu.insertAdjacentHTML('afterend', pickerHtml() + '<div class="dropdown-divider"></div>');
+35
View File
@@ -373,6 +373,41 @@ div.tooltip:hover span.tip {
} }
div.tooltip span.tip strong { color: var(--md-sys-color-on-surface); font-weight: 500; } div.tooltip span.tip strong { color: var(--md-sys-color-on-surface); font-weight: 500; }
div.tooltip span.tip .avatar { vertical-align: middle; } div.tooltip span.tip .avatar { vertical-align: middle; }
/* Structured by theme.js (structureTip): title block, then a label/value grid. */
div.tooltip span.nm-tip .nm-tip__title {
display: flex;
flex-direction: column;
gap: 2px;
margin-block-end: 12px;
}
div.tooltip span.nm-tip .nm-tip__title > a {
font: var(--md-sys-typescale-label-medium);
letter-spacing: var(--md-sys-typescale-label-medium-tracking);
text-decoration: none;
}
div.tooltip span.nm-tip .nm-tip__title > span {
color: var(--md-sys-color-on-surface);
font: var(--md-sys-typescale-title-small);
letter-spacing: var(--md-sys-typescale-title-small-tracking);
}
div.tooltip span.nm-tip .nm-tip__fields {
display: grid;
grid-template-columns: auto 1fr;
gap: 4px 16px;
align-items: center;
}
div.tooltip span.nm-tip .nm-tip__fields > strong {
color: var(--md-sys-color-on-surface-variant);
font: var(--md-sys-typescale-label-medium);
letter-spacing: var(--md-sys-typescale-label-medium-tracking);
}
div.tooltip span.nm-tip .nm-tip__fields > span {
color: var(--md-sys-color-on-surface);
display: flex;
align-items: center;
gap: 6px;
min-inline-size: 0;
}
/***** jQuery UI: dialogs, date picker, menus *****/ /***** jQuery UI: dialogs, date picker, menus *****/
.ui-widget, .ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: var(--md-ref-typeface-plain); } .ui-widget, .ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: var(--md-ref-typeface-plain); }