HTML Tree: Display a tooltip with full cell contents when truncated

Closes #2321

Also added tooltips for cells that use an icon in their contents
This commit is contained in:
Adomas Venčkauskas 2022-02-03 16:07:03 +02:00
parent 4777f0ec89
commit 8de1ddcaf4
4 changed files with 58 additions and 3 deletions

View file

@ -682,6 +682,7 @@ class VirtualizedTable extends React.Component {
if (!modifierClick && !this.selection.isSelected(index)) { if (!modifierClick && !this.selection.isSelected(index)) {
this._onSelection(index, false, false); this._onSelection(index, false, false);
} }
this.focus();
} }
_handleMouseUp = async (e, index) => { _handleMouseUp = async (e, index) => {
@ -693,6 +694,7 @@ class VirtualizedTable extends React.Component {
return; return;
} }
this._onSelection(index, shiftSelect, toggleSelection); this._onSelection(index, shiftSelect, toggleSelection);
this.focus();
} }
_activateNode = (event, indices) => { _activateNode = (event, indices) => {
@ -850,6 +852,32 @@ class VirtualizedTable extends React.Component {
return [aColumn, bColumn, resizingColumn]; return [aColumn, bColumn, resizingColumn];
} }
/**
* Toggle [title] attribute on cells when text is truncated
* so that a tooltip gets displayed on hover.
* @param event
*/
_handleMouseOver = (event) => {
let elem = event.target;
if (!elem.classList.contains('cell') || elem.classList.contains('cell-icon')) return;
let textElem = elem.querySelector('.label, .cell-text');
// .label is used in the header, .cell-text on primary cells,
// otherwise the .cell element if its immediate child is a text node
// should be used.
if (!textElem) {
if (elem.childNodes[0].nodeType != window.Node.TEXT_NODE) return;
textElem = elem;
}
// We need to set the [title] attribute on the .label element in the header
if (textElem.classList.contains('label')) elem = textElem;
if (textElem.offsetWidth < textElem.scrollWidth) {
elem.setAttribute('title', textElem.textContent);
}
else {
elem.removeAttribute('title');
}
}
_handleResizerDragStop = (event) => { _handleResizerDragStop = (event) => {
event.stopPropagation(); event.stopPropagation();
const result = this._getResizeColumns(); const result = this._getResizeColumns();
@ -943,6 +971,7 @@ class VirtualizedTable extends React.Component {
this.props.treeboxRef && this.props.treeboxRef(this._jsWindow); this.props.treeboxRef && this.props.treeboxRef(this._jsWindow);
this._setAlternatingRows(); this._setAlternatingRows();
this._setXulTooltip();
window.addEventListener("resize", () => { window.addEventListener("resize", () => {
this._debouncedRerender(); this._debouncedRerender();
@ -959,6 +988,31 @@ class VirtualizedTable extends React.Component {
this.forceUpdate(); this.forceUpdate();
} }
} }
/**
* Make HTML [title] attribute display a tooltip. Without this
* HTML [title] attribute when embedded in a XUL window does not
* trigger a tooltip to be displayed
* @private
*/
_setXulTooltip() {
// Make sure container xul element has a tooltip set
let xulElem = this._topDiv.closest('.virtualized-table-container');
if (xulElem.getAttribute('tooltip') != 'html-tooltip') {
xulElem.setAttribute('tooltip', 'html-tooltip');
}
if (document.querySelector('tooltip#html-tooltip')) return;
let tooltip = document.createElement('tooltip');
tooltip.id = 'html-tooltip';
tooltip.addEventListener('popupshowing', function(e) {
let tooltipTitleNode = document.tooltipNode.closest('div *[title], iframe *[title], browser *[title]');
if (document.tooltipNode && tooltipTitleNode) {
this.setAttribute('label', tooltipTitleNode.getAttribute('title'));
}
e.preventDefault();
});
document.documentElement.appendChild(tooltip);
}
_setAlternatingRows() { _setAlternatingRows() {
if (this.props.alternatingRowColors) { if (this.props.alternatingRowColors) {
@ -1052,7 +1106,7 @@ class VirtualizedTable extends React.Component {
<span <span
key={columnName + '-label'} key={columnName + '-label'}
className={`label ${column.dataKey}`} className={`label ${column.dataKey}`}
title={columnName}> title={column.iconLabel ? columnName : ""}>
{label} {label}
</span> </span>
{sortIndicator} {sortIndicator}
@ -1081,6 +1135,7 @@ class VirtualizedTable extends React.Component {
onDragOver: this._onDragOver, onDragOver: this._onDragOver,
onDrop: e => this.props.onDrop && this.props.onDrop(e), onDrop: e => this.props.onDrop && this.props.onDrop(e),
onFocus: e => this.props.onFocus && this.props.onFocus(e), onFocus: e => this.props.onFocus && this.props.onFocus(e),
onMouseOver: e => this._handleMouseOver(e),
className: cx(["virtualized-table", { resizing: this.state.resizing }]), className: cx(["virtualized-table", { resizing: this.state.resizing }]),
id: this.props.id, id: this.props.id,
ref: ref => this._topDiv = ref, ref: ref => this._topDiv = ref,

View file

@ -2684,6 +2684,7 @@ var ItemTree = class ItemTree extends LibraryTree {
//} //}
if (ariaLabel) { if (ariaLabel) {
icon.setAttribute('aria-label', ariaLabel + '.'); icon.setAttribute('aria-label', ariaLabel + '.');
span.setAttribute('title', ariaLabel);
} }
span.append(icon); span.append(icon);

View file

@ -358,7 +358,7 @@
</splitter> </splitter>
<box id="zotero-layout-switcher" orient="horizontal" zotero-persist="orient" flex="1"> <box id="zotero-layout-switcher" orient="horizontal" zotero-persist="orient" flex="1">
<hbox id="zotero-items-pane" class="virtualized-table-container" zotero-persist="width height" flex="1" clickthrough="never" tooltip="html-tooltip"> <hbox id="zotero-items-pane" class="virtualized-table-container" zotero-persist="width height" flex="1" clickthrough="never">
<html:div id="zotero-items-tree"></html:div> <html:div id="zotero-items-tree"></html:div>
</hbox> </hbox>

View file

@ -219,7 +219,6 @@
padding: 2px 5px; padding: 2px 5px;
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden; overflow: hidden;
pointer-events: none;
max-height: 100%; max-height: 100%;
} }
} }