d40ace87860e49440a8ffcd09a4a68a682ad07ec
chmalee
Thu Apr 23 12:57:12 2026 -0700
add nowrap rules to more settings in the hubSpace data table to prevent row heights from growing when a filename is too long for the current window size and forces an adjustment by Data Tables. adjust columns when the window size grows dynamically. this should ensure the view in buttons are always the same size regardless of screen width or table content, refs Max/Baihe email
diff --git src/hg/js/hgMyData.js src/hg/js/hgMyData.js
index 143b8b3f6be..d9298b42ef0 100644
--- src/hg/js/hgMyData.js
+++ src/hg/js/hgMyData.js
@@ -1185,31 +1185,30 @@
});
}
});
updateSelectedFileDiv(data, selectedRow.data().fileType === "dir");
}
function createOneCrumb(table, dirName, dirFullPath, doAddEvent) {
// make a new span that can be clicked to nav through the table
let newSpan = document.createElement("span");
newSpan.id = dirName;
newSpan.textContent = decodeURIComponent(dirName);
newSpan.classList.add("breadcrumb");
if (doAddEvent) {
newSpan.addEventListener("click", function(e) {
dataTableShowDir(table, dirName, dirFullPath);
- table.draw();
dataTableCustomOrder(table, {"fullPath": dirFullPath});
table.draw();
});
} else {
// can't click the final crumb so don't underline it
newSpan.style.textDecoration = "unset";
}
return newSpan;
}
function dataTableEmptyBreadcrumb(table) {
let currBreadcrumb = document.getElementById("breadcrumb");
currBreadcrumb.replaceChildren(currBreadcrumb.firstChild);
}
@@ -1248,32 +1247,32 @@
clearSearch(table);
// deselect any selected rows like Finder et al when moving into/upto a directory
table.rows({selected: true}).deselect();
table.search.fixed("showRoot", function(searchStr, rowData, rowIx) {
return !rowData.parentDir;
});
uiState.currentHub = "";
hideHubBanner();
}
function dataTableShowDir(table, dirName, dirFullPath) {
// show the directory and all immediate children of the directory
clearSearch(table);
// deselect any selected rows like Finder et al when moving into/upto a directory
table.rows({selected: true}).deselect();
- table.draw();
- // NOTE that the below does not actually render until the next table.draw() call
+ // Callers must call table.draw() after this so filter + order changes
+ // from showDir/customOrder render in a single redraw.
table.search.fixed("oneHub", function(searchStr, rowData, rowIx) {
// calculate the fullPath of this rows parentDir in case the dirName passed
// to this function has the same name as a parentDir further up in the
// listing. For example, consider a test/test/tmp.txt layout, where "test"
// is the parentDir of tmp.txt and the test subdirectory
let parentDirFull = rowData.fullPath.split("/").slice(0,-1).join("/");
if (rowData.parentDir === dirName && parentDirFull === dirFullPath) {
return true;
} else if (rowData.fullPath === dirFullPath) {
// also return the directory itself
return true;
} else {
return false;
}
});
@@ -1431,30 +1430,31 @@
console.log("folder click");
let table = $("#filesTable").DataTable();
let trow = $(e.target).closest("tr");
let row = table.row(trow);
dataTableShowDir(table, rowData.fileName, rowData.fullPath);
dataTableCustomOrder(table, rowData);
table.draw();
});
return folderIcon;
} else {
// only offer the button if this is a track file
if (rowData.fileType !== "hub.txt" && rowData.fileType !== "text" && rowData.fileType !== "tabixIndex" && rowData.fileType !== "bamIndex" && rowData.fileType !== "2bit" && rowData.fileType in extensionMap) {
let container = document.createElement("div");
let viewBtn = document.createElement("button");
viewBtn.textContent = "View in Genome Browser";
+ viewBtn.style.whiteSpace = "nowrap";
viewBtn.type = 'button';
viewBtn.addEventListener("click", function(e) {
e.stopPropagation();
viewInGenomeBrowser(rowData.fileName, rowData.fileType, rowData.genome, rowData.parentDir, rowData.hubType);
});
container.appendChild(viewBtn);
return container;
} else {
return null;
}
}
}
function deleteFileFromTable(pathList) {
// req is an object with properties of an uploaded file, make a new row
@@ -1536,63 +1536,59 @@
for (let j = 0; j < allRows.length; j++) {
let rowData = table.row(allRows[j]).data();
if (rowData.fullPath === obj.fullPath) {
table.row(allRows[j]).invalidate();
break;
}
}
}
}
// show all the new rows we just added, note the double draw, we need
// to have the new rows rendered to do the order because the order
// will copy the actual DOM node
parseFileListIntoHash(uiState.fileList);
dataTableShowDir(table, hubDirData.fileName, hubDirData.fullPath);
- table.draw();
dataTableCustomOrder(table, hubDirData);
- // Flush dataTableCustomOrder's row.remove() so DataTables internal state is clean,
- // then defer columns.adjust() to allow browser reflow after header DOM manipulation
table.draw();
- setTimeout(function() {
- table.columns.adjust();
- }, 0);
}
function doRowSelect(evtype, table, indexes) {
let selectedRow = table.row(indexes);
let rowTr = selectedRow.node();
if (rowTr) {
handleCheckboxSelect(evtype, table, selectedRow);
}
}
function indentActionButton(rowTr, rowData) {
let numIndents = "0px"; //data.parentDir !== "" ? data.fullPath.split('/').length - 1: 0;
if (rowData.fileType !== "dir") {
numIndents = "10px";
}
rowTr.childNodes[1].style.textIndent = numIndents;
}
let tableInitOptions = {
select: {
items: 'row',
selector: 'td:first-child',
style: 'multi+shift', // default to a single click is all that's needed
},
pageLength: 25,
+ autoWidth: false, // let the browser's default table-layout:auto size columns,
+ // so they shrink/grow with the container on window resize
scrollY: 600,
scrollCollapse: true, // when less than scrollY height is needed, make the table shorter
deferRender: true, // only draw into the DOM the nodes we need for each page
orderCellsTop: true, // when viewing a subdirectory, the directory becomes a part of
// the header, this option prevents those cells from being used to
// sort the table
layout: {
top2Start: {
div: {
className: "",
id: "breadcrumb",
html: "My Data",
}
},
topStart: {
@@ -1622,63 +1618,63 @@
return '';
}
},
{
targets: 2,
render: function(data, type, row, meta) {
let decodedName = decodeURIComponent(data);
if (type !== "display" || row.fileType === "dir") {
return decodedName;
}
if (typeof uiState.userUrl === "undefined" || uiState.userUrl.length === 0) {
return decodedName;
}
let fileUrl = uiState.userUrl + cgiEncode(row.fullPath);
let copyIcon = '';
- return '' + decodedName + '' + copyIcon;
+ return '' + decodedName + '' + copyIcon + '';
}
},
{
targets: 3,
render: function(data, type, row, meta) {
if (type === "display") {
return dataTablePrintSize(data, type, row, meta);
}
return data;
}
},
{
targets: 5,
render: function(data, type, row) {
if (type === "display") {
return dataTablePrintGenome(data);
}
return data;
}
},
{
targets: 6,
render: function(data, type, row) {
if (type === "display") {
return cgiDecode(data);
}
return data;
}
},
{
- // The upload time column
- targets: 8,
+ targets: [7, 8],
+ className: "nowrap",
visible: true,
searchable: false,
orderable: true,
},
{
targets: 9,
visible: false,
searchable: false,
orderable: true,
}
],
columns: [
{data: "", },
{data: "", },
{data: "fileName", title: "File name"},
@@ -1732,30 +1728,38 @@
if (uiState.isLoggedIn) {
tableInitOptions.language = {emptyTable: "Uploaded files will appear here. Click \"Upload\" to get started"};
} else {
tableInitOptions.language = {emptyTable: "You are not logged in, please log in or create an account to begin uploading files"};
}
DataTable.feature.register('quota', function(settings, opts) {
let options = Object.assign({option1: false, option2: false}, opts);
let container = document.createElement("div");
container.id = "quotaDiv";
if (uiState.isLoggedIn) {
container.textContent = `Using ${prettyFileSize(uiState.userQuota)} of ${prettyFileSize(uiState.maxQuota)}`;
}
return container;
});
let table = new DataTable("#filesTable", tableInitOptions);
+ // Re-sync the scrollY head/body column widths after a resize settles.
+ let resizeTimer = null;
+ window.addEventListener("resize", function() {
+ clearTimeout(resizeTimer);
+ resizeTimer = setTimeout(function() {
+ table.columns.adjust();
+ }, 100);
+ });
if (uiState.isLoggedIn) {
table.buttons(".uploadButton").enable();
document.getElementById("rootBreadcrumb").addEventListener("click", function(e) {
dataTableShowTopLevel(table);
dataTableCustomOrder(table);
dataTableEmptyBreadcrumb(table);
table.draw();
});
} else {
table.buttons(".uploadButton").disable();
}
let hubBannerBtn = document.getElementById("hubBannerViewBtn");
if (hubBannerBtn) {
hubBannerBtn.addEventListener("click", function(e) {
viewHubInGenomeBrowser(uiState.currentHub);
@@ -1819,31 +1823,30 @@
}
}
}
});
return table;
}
function handleGetFileList(jsonData, textStatus) {
_.assign(uiState, jsonData.userFiles);
if (uiState.fileList) {
parseFileListIntoHash(uiState.fileList);
}
// first add the top level directories/files
let table = showExistingFiles(uiState.fileList);
- table.columns.adjust().draw();
uppy.use(Uppy.Dashboard, uppyOptions);
// define this in init so globals are available at runtime
let tusOptions = {
endpoint: getTusdEndpoint(),
withCredentials: true,
retryDelays: null,
removeFingerprintOnSuccess: true, // clean up localStorage after successful upload
};
uppy.use(Uppy.Tus, tusOptions);
uppy.use(BatchChangePlugin, {target: Uppy.Dashboard});
uppy.on('upload-success', (file, response) => {
const metadata = file.meta;