diff --git a/tonalmine/javascripts/theme.js b/tonalmine/javascripts/theme.js index 755c7c8..9dc6cd2 100644 --- a/tonalmine/javascripts/theme.js +++ b/tonalmine/javascripts/theme.js @@ -133,6 +133,40 @@ }); }); + // Calendar/Gantt hover cards: Redmine renders "Tracker #id: subject

" followed by + // "Label: value
" 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 () { var menu = document.querySelector('#account .dropdown-content .dropdown-divider'); if (menu) menu.insertAdjacentHTML('afterend', pickerHtml() + ''); diff --git a/tonalmine/stylesheets/components.css b/tonalmine/stylesheets/components.css index a4f64b6..3bc5408 100644 --- a/tonalmine/stylesheets/components.css +++ b/tonalmine/stylesheets/components.css @@ -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 .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 *****/ .ui-widget, .ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: var(--md-ref-typeface-plain); }