Improve column resizing robustness (#2588)
Also: * Add staticWidth and minWidth column properties
This commit is contained in:
parent
01645c5e51
commit
c49a05d486
5 changed files with 25 additions and 33 deletions
|
@ -36,6 +36,9 @@ const { IconDownChevron, getDOMElement } = require('components/icons');
|
||||||
const TYPING_TIMEOUT = 1000;
|
const TYPING_TIMEOUT = 1000;
|
||||||
const MINIMUM_ROW_HEIGHT = 20; // px
|
const MINIMUM_ROW_HEIGHT = 20; // px
|
||||||
const RESIZER_WIDTH = 5; // px
|
const RESIZER_WIDTH = 5; // px
|
||||||
|
const COLUMN_MIN_WIDTH = 20;
|
||||||
|
const COLUMN_NORMALIZATION_WIDTH = 8192;
|
||||||
|
const COLUMN_PADDING = 10; // N.B. MUST BE INLINE WITH CSS!!!
|
||||||
|
|
||||||
const noop = () => 0;
|
const noop = () => 0;
|
||||||
|
|
||||||
|
@ -760,21 +763,6 @@ class VirtualizedTable extends React.Component {
|
||||||
|
|
||||||
// ------------------------ Column Methods ------------------------- //
|
// ------------------------ Column Methods ------------------------- //
|
||||||
|
|
||||||
/**
|
|
||||||
* A public function to update the column CSS flex widths. To be used
|
|
||||||
* upon window resize and similar. Especially important on macOS where
|
|
||||||
* column borders are used and get out of sync with column headers.
|
|
||||||
*/
|
|
||||||
updateColumnWidths = Zotero.Utilities.debounce(() => {
|
|
||||||
let resizeData = {};
|
|
||||||
const columns = this._getVisibleColumns();
|
|
||||||
for (const column of columns) {
|
|
||||||
const elem = document.querySelector(`#${this.props.id} .virtualized-table-header .cell.${column.dataKey}`)
|
|
||||||
resizeData[column.dataKey] = elem.getBoundingClientRect().width;
|
|
||||||
}
|
|
||||||
this._columns.onResize(resizeData, true);
|
|
||||||
}, 200)
|
|
||||||
|
|
||||||
_handleResizerDragStart = (index, event) => {
|
_handleResizerDragStart = (index, event) => {
|
||||||
if (event.button !== 0) return false;
|
if (event.button !== 0) return false;
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
@ -811,8 +799,9 @@ class VirtualizedTable extends React.Component {
|
||||||
offset += resizingRect.width;
|
offset += resizingRect.width;
|
||||||
}
|
}
|
||||||
const widthSum = aRect.width + bRect.width;
|
const widthSum = aRect.width + bRect.width;
|
||||||
// Column min-width: 20px;
|
const aSpacingOffset = (aColumn.minWidth ? aColumn.minWidth : COLUMN_MIN_WIDTH) + COLUMN_PADDING;
|
||||||
const aColumnWidth = Math.min(widthSum - 20, Math.max(20, event.clientX - (RESIZER_WIDTH / 2) - offset));
|
const bSpacingOffset = (bColumn.minWidth ? bColumn.minWidth : COLUMN_MIN_WIDTH) + COLUMN_PADDING;
|
||||||
|
const aColumnWidth = Math.min(widthSum - bSpacingOffset, Math.max(aSpacingOffset, event.clientX - (RESIZER_WIDTH / 2) - offset));
|
||||||
const bColumnWidth = widthSum - aColumnWidth;
|
const bColumnWidth = widthSum - aColumnWidth;
|
||||||
let onResizeData = {};
|
let onResizeData = {};
|
||||||
onResizeData[aColumn.dataKey] = aColumnWidth;
|
onResizeData[aColumn.dataKey] = aColumnWidth;
|
||||||
|
@ -1477,6 +1466,8 @@ var Columns = class {
|
||||||
if (storePrefs) {
|
if (storePrefs) {
|
||||||
var prefs = this._getPrefs();
|
var prefs = this._getPrefs();
|
||||||
}
|
}
|
||||||
|
const header = document.querySelector(`#${this._styleKey} .virtualized-table-header`);
|
||||||
|
const headerWidth = header ? header.getBoundingClientRect().width : 300;
|
||||||
for (let [dataKey, width] of Object.entries(columnWidths)) {
|
for (let [dataKey, width] of Object.entries(columnWidths)) {
|
||||||
if (typeof dataKey == "number") {
|
if (typeof dataKey == "number") {
|
||||||
dataKey = this._columns[dataKey].dataKey;
|
dataKey = this._columns[dataKey].dataKey;
|
||||||
|
@ -1487,11 +1478,15 @@ var Columns = class {
|
||||||
column.width = width;
|
column.width = width;
|
||||||
prefs[dataKey] = this._getColumnPrefsToPersist(column);
|
prefs[dataKey] = this._getColumnPrefsToPersist(column);
|
||||||
}
|
}
|
||||||
if (column.fixedWidth && column.width) {
|
if (column.fixedWidth) {
|
||||||
|
width = column.width;
|
||||||
|
}
|
||||||
|
if (column.fixedWidth && column.width || column.staticWidth) {
|
||||||
this._stylesheet.sheet.cssRules[styleIndex].style.setProperty('flex', `0 0`, `important`);
|
this._stylesheet.sheet.cssRules[styleIndex].style.setProperty('flex', `0 0`, `important`);
|
||||||
this._stylesheet.sheet.cssRules[styleIndex].style.setProperty('max-width', `${column.width}px`, 'important');
|
this._stylesheet.sheet.cssRules[styleIndex].style.setProperty('max-width', `${width}px`, 'important');
|
||||||
this._stylesheet.sheet.cssRules[styleIndex].style.setProperty('min-width', `${column.width}px`, 'important');
|
this._stylesheet.sheet.cssRules[styleIndex].style.setProperty('min-width', `${width}px`, 'important');
|
||||||
} else {
|
} else {
|
||||||
|
width = (width - COLUMN_PADDING) * COLUMN_NORMALIZATION_WIDTH / headerWidth;
|
||||||
this._stylesheet.sheet.cssRules[styleIndex].style.setProperty('flex-basis', `${width}px`);
|
this._stylesheet.sheet.cssRules[styleIndex].style.setProperty('flex-basis', `${width}px`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2734,7 +2734,7 @@ var ItemTree = class ItemTree extends LibraryTree {
|
||||||
const depth = this.getLevel(index);
|
const depth = this.getLevel(index);
|
||||||
let firstChildIndent = 0;
|
let firstChildIndent = 0;
|
||||||
if (column.ordinal == 0) {
|
if (column.ordinal == 0) {
|
||||||
firstChildIndent = 6;
|
firstChildIndent = 5;
|
||||||
}
|
}
|
||||||
span.style.paddingInlineStart = ((CHILD_INDENT * depth) + firstChildIndent) + 'px';
|
span.style.paddingInlineStart = ((CHILD_INDENT * depth) + firstChildIndent) + 'px';
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,9 @@ const Icons = require('components/icons');
|
||||||
* flex: number, // Default: 1. When the column is added to the tree how much space it should occupy as a flex ratio
|
* flex: number, // Default: 1. When the column is added to the tree how much space it should occupy as a flex ratio
|
||||||
* width: string, // A column width instead of flex ratio. See above.
|
* width: string, // A column width instead of flex ratio. See above.
|
||||||
* fixedWidth: boolean // Default: false. Set to true to disable column resizing
|
* fixedWidth: boolean // Default: false. Set to true to disable column resizing
|
||||||
|
* staticWidth: boolean // Default: false. Set to true to prevent columns from changing width when
|
||||||
|
* // the width of the tree increases or decreases
|
||||||
|
* minWidth: number, // Override the default [20px] column min-width for resizing
|
||||||
*
|
*
|
||||||
* label: string, // The column label. Either a string or the id to an i18n string.
|
* label: string, // The column label. Either a string or the id to an i18n string.
|
||||||
* iconLabel: React.Component, // Set an Icon label instead of a text-based one
|
* iconLabel: React.Component, // Set an Icon label instead of a text-based one
|
||||||
|
@ -86,6 +89,7 @@ const COLUMNS = [
|
||||||
defaultSort: -1,
|
defaultSort: -1,
|
||||||
label: "zotero.items.year_column",
|
label: "zotero.items.year_column",
|
||||||
flex: 1,
|
flex: 1,
|
||||||
|
staticWidth: true,
|
||||||
zoteroPersist: new Set(["width", "hidden", "sortDirection"])
|
zoteroPersist: new Set(["width", "hidden", "sortDirection"])
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -298,6 +302,8 @@ const COLUMNS = [
|
||||||
label: "zotero.tabs.notes.label",
|
label: "zotero.tabs.notes.label",
|
||||||
iconLabel: <Icons.IconTreeitemNoteSmall />,
|
iconLabel: <Icons.IconTreeitemNoteSmall />,
|
||||||
width: "14",
|
width: "14",
|
||||||
|
minWidth: 14,
|
||||||
|
staticWidth: true,
|
||||||
zoteroPersist: new Set(["width", "hidden", "sortDirection"])
|
zoteroPersist: new Set(["width", "hidden", "sortDirection"])
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
|
@ -108,7 +108,6 @@ var ZoteroPane = new function()
|
||||||
if (!tabsDeck || tabsDeck.getAttribute('selectedIndex') == 0) {
|
if (!tabsDeck || tabsDeck.getAttribute('selectedIndex') == 0) {
|
||||||
this.updateToolbarPosition();
|
this.updateToolbarPosition();
|
||||||
this.updateTagsBoxSize();
|
this.updateTagsBoxSize();
|
||||||
this.updateItemTreeColumnWidths();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
window.setTimeout(this.updateToolbarPosition.bind(this), 0);
|
window.setTimeout(this.updateToolbarPosition.bind(this), 0);
|
||||||
|
@ -5485,11 +5484,6 @@ var ZoteroPane = new function()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.updateItemTreeColumnWidths = function() {
|
|
||||||
if (!ZoteroPane.itemsView || !ZoteroPane.itemsView.tree) return;
|
|
||||||
ZoteroPane.itemsView.tree.updateColumnWidths();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens the about dialog
|
* Opens the about dialog
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -39,10 +39,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.cell:first-child {
|
|
||||||
padding-inline-start: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cell {
|
.cell {
|
||||||
min-width: 30px;
|
min-width: 30px;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
|
@ -63,7 +59,7 @@
|
||||||
flex-shrink: 1;
|
flex-shrink: 1;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
margin-inline-start: 6px;
|
margin-inline-start: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.twisty + .cell-text, .spacer-twisty + .cell-text {
|
.twisty + .cell-text, .spacer-twisty + .cell-text {
|
||||||
|
@ -178,6 +174,7 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
padding: 0 5px;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
|
@ -199,7 +196,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.label {
|
.label {
|
||||||
margin-inline-start: 10px;
|
margin-inline-start: 5px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|
Loading…
Reference in a new issue