mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2024-11-24 10:16:00 +03:00
WebUI: Improve table overflow handling
This PR relies on flexbox to ensure all WebUI tables are the correct height without overflowing. Table headers are now always visible and JS-based dynamic resizing is no longer needed. PR #21652.
This commit is contained in:
parent
b083029841
commit
dc30b9c2ec
9 changed files with 65 additions and 73 deletions
|
@ -246,6 +246,7 @@ li.divider {
|
|||
}
|
||||
|
||||
.pad {
|
||||
height: 100%;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,11 @@
|
|||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#transferList #transferList_pad {
|
||||
/* override for default mocha inline style */
|
||||
display: flex !important;
|
||||
}
|
||||
|
||||
tr.dynamicTableHeader {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
@ -82,7 +87,14 @@ tr.dynamicTableHeader {
|
|||
padding-left: 25px;
|
||||
}
|
||||
|
||||
div:has(> div.dynamicTableFixedHeaderDiv):not(.invisible) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.dynamicTableFixedHeaderDiv {
|
||||
flex-shrink: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
@ -92,6 +104,7 @@ tr.dynamicTableHeader {
|
|||
}
|
||||
|
||||
.dynamicTableDiv {
|
||||
flex-grow: 1;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
|
|
|
@ -270,6 +270,14 @@ a.propButton img {
|
|||
overflow: hidden auto;
|
||||
}
|
||||
|
||||
.propertiesTabContent {
|
||||
height: 100%;
|
||||
|
||||
> div {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/* context menu specific */
|
||||
|
||||
.contextMenu {
|
||||
|
|
|
@ -452,7 +452,7 @@
|
|||
</div>
|
||||
<input id="closeButton" type="button" value="Close" style="float: right; width: 30%;">
|
||||
</div>
|
||||
<div id="torrentFiles" class="panel" style="position: absolute; top: 0; right: 0; bottom: 0; left: 228px; margin: 35px 10px 45px 20px; border-bottom: 0">
|
||||
<div id="torrentFiles" class="panel" style="position: absolute; top: 0; right: 0; bottom: 0; left: 228px; margin: 35px 10px 45px 20px; border-bottom: 0; height: initial;">
|
||||
<div id="bulkRenameFilesTableFixedHeaderDiv" class="dynamicTableFixedHeaderDiv">
|
||||
<table class="dynamicTable">
|
||||
<thead>
|
||||
|
|
|
@ -100,33 +100,6 @@ window.qBittorrent.DynamicTable ??= (() => {
|
|||
tableDiv.addEventListener("scroll", () => {
|
||||
tableElement.style.left = `${-tableDiv.scrollLeft}px`;
|
||||
});
|
||||
|
||||
// if the table exists within a panel
|
||||
const parentPanel = tableDiv.getParent(".panel");
|
||||
if (parentPanel) {
|
||||
const resizeFn = (entries) => {
|
||||
const panel = entries[0].target;
|
||||
let h = panel.getBoundingClientRect().height - tableFixedHeaderDiv.getBoundingClientRect().height;
|
||||
tableDiv.style.height = `${h}px`;
|
||||
|
||||
// Workaround due to inaccurate calculation of elements heights by browser
|
||||
let n = 2;
|
||||
|
||||
// is panel vertical scrollbar visible or does panel content not fit?
|
||||
while (((panel.clientWidth !== panel.offsetWidth) || (panel.clientHeight !== panel.scrollHeight)) && (n > 0)) {
|
||||
--n;
|
||||
h -= 0.5;
|
||||
tableDiv.style.height = `${h}px`;
|
||||
}
|
||||
};
|
||||
|
||||
const resizeDebouncer = window.qBittorrent.Misc.createDebounceHandler(100, (entries) => {
|
||||
resizeFn(entries);
|
||||
});
|
||||
|
||||
const resizeObserver = new ResizeObserver(resizeDebouncer);
|
||||
resizeObserver.observe(parentPanel, { box: "border-box" });
|
||||
}
|
||||
},
|
||||
|
||||
setupHeaderEvents: function() {
|
||||
|
|
|
@ -205,10 +205,10 @@ window.qBittorrent.Search ??= (() => {
|
|||
|
||||
// unhide the results elements
|
||||
if (numSearchTabs() >= 1) {
|
||||
$("searchResultsNoSearches").style.display = "none";
|
||||
$("searchResultsFilters").style.display = "block";
|
||||
$("searchResultsTableContainer").style.display = "block";
|
||||
$("searchTabsToolbar").style.display = "block";
|
||||
$("searchResultsNoSearches").classList.add("invisible");
|
||||
$("searchResultsFilters").classList.remove("invisible");
|
||||
$("searchResultsTableContainer").classList.remove("invisible");
|
||||
$("searchTabsToolbar").classList.remove("invisible");
|
||||
}
|
||||
|
||||
// select new tab
|
||||
|
@ -271,10 +271,10 @@ window.qBittorrent.Search ??= (() => {
|
|||
|
||||
$("numSearchResultsVisible").textContent = 0;
|
||||
$("numSearchResultsTotal").textContent = 0;
|
||||
$("searchResultsNoSearches").style.display = "block";
|
||||
$("searchResultsFilters").style.display = "none";
|
||||
$("searchResultsTableContainer").style.display = "none";
|
||||
$("searchTabsToolbar").style.display = "none";
|
||||
$("searchResultsNoSearches").classList.remove("invisible");
|
||||
$("searchResultsFilters").classList.add("invisible");
|
||||
$("searchResultsTableContainer").classList.add("invisible");
|
||||
$("searchTabsToolbar").classList.add("invisible");
|
||||
}
|
||||
else if (isTabSelected && newTabToSelect) {
|
||||
setActiveTab(newTabToSelect);
|
||||
|
@ -670,9 +670,9 @@ window.qBittorrent.Search ??= (() => {
|
|||
|
||||
const searchPluginsEmpty = (searchPlugins.length === 0);
|
||||
if (!searchPluginsEmpty) {
|
||||
$("searchResultsNoPlugins").style.display = "none";
|
||||
$("searchResultsNoPlugins").classList.add("invisible");
|
||||
if (numSearchTabs() === 0)
|
||||
$("searchResultsNoSearches").style.display = "block";
|
||||
$("searchResultsNoSearches").classList.remove("invisible");
|
||||
|
||||
// sort plugins alphabetically
|
||||
const allPlugins = searchPlugins.sort((left, right) => {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<style type="text/css">
|
||||
#logTopBar {
|
||||
flex-shrink: 0;
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
|
@ -22,13 +23,16 @@
|
|||
}
|
||||
|
||||
#logView {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
padding: 0 20px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#logContentView {
|
||||
display: block;
|
||||
vertical-align: top;
|
||||
flex-grow: 1;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#logMessageTableFixedHeaderDiv .dynamicTableHeader,
|
||||
|
@ -227,7 +231,6 @@
|
|||
tableInfo["peer"].instance.setup("logPeerTableDiv", "logPeerTableFixedHeaderDiv", logTableContextMenu);
|
||||
|
||||
MUI.Panels.instances.LogPanel.contentEl.style.height = "100%";
|
||||
$("logView").style.height = "inherit";
|
||||
|
||||
load();
|
||||
};
|
||||
|
|
|
@ -2,13 +2,18 @@
|
|||
#rssView {
|
||||
padding: 20px 20px 0 20px;
|
||||
height: calc(100% - 20px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#rssTopBar {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
#rssContentView {
|
||||
display: table;
|
||||
width: 100%;
|
||||
height: calc(100% - 30px);
|
||||
vertical-align: top;
|
||||
flex-grow: 1;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#rssFeedFixedHeaderDiv .dynamicTableHeader,
|
||||
|
@ -45,6 +50,7 @@
|
|||
|
||||
#rightRssColumn {
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#rssFeedTableDiv,
|
||||
|
@ -53,17 +59,21 @@
|
|||
}
|
||||
|
||||
#rssTorrentDetailsName {
|
||||
flex-shrink: 0;
|
||||
background-color: var(--color-background-blue);
|
||||
padding: 0;
|
||||
color: var(--color-text-white);
|
||||
}
|
||||
|
||||
#rssTorrentDetailsDate {
|
||||
flex-shrink: 1;
|
||||
background-color: var(--color-background-default);
|
||||
}
|
||||
|
||||
#rssDetailsView {
|
||||
height: calc(100vh - 135px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
|
@ -85,6 +95,7 @@
|
|||
}
|
||||
|
||||
#rssDescription {
|
||||
flex-grow: 2;
|
||||
width: 100%;
|
||||
border: none;
|
||||
}
|
||||
|
@ -206,18 +217,6 @@
|
|||
if (!pref.rss_processing_enabled)
|
||||
$("rssFetchingDisabled").removeClass("invisible");
|
||||
|
||||
// recalculate heights
|
||||
const nonPageHeight = $("rssTopBar").getBoundingClientRect().height
|
||||
+ $("desktopHeader").getBoundingClientRect().height
|
||||
+ $("desktopFooterWrapper").getBoundingClientRect().height + 20;
|
||||
$("rssDetailsView").style.height = "calc(100vh - " + nonPageHeight + "px)";
|
||||
|
||||
const nonTableHeight = nonPageHeight + $("rssFeedFixedHeaderDiv").getBoundingClientRect().height;
|
||||
$("rssFeedTableDiv").style.height = "calc(100vh - " + nonTableHeight + "px)";
|
||||
$("rssArticleTableDiv").style.height = "calc(100vh - " + nonTableHeight + "px)";
|
||||
|
||||
$("rssContentView").style.height = "calc(100% - " + $("rssTopBar").getBoundingClientRect().height + "px)";
|
||||
|
||||
const rssFeedContextMenu = new window.qBittorrent.ContextMenu.RssFeedContextMenu({
|
||||
targets: ".rssFeedContextMenuTarget",
|
||||
menu: "rssFeedMenu",
|
||||
|
@ -451,11 +450,6 @@
|
|||
const torrentDescription = document.createRange().createContextualFragment('<iframe sandbox id="rssDescription"></iframe>');
|
||||
$("rssDetailsView").append(torrentDescription);
|
||||
document.getElementById("rssDescription").srcdoc = '<html><head><link rel="stylesheet" type="text/css" href="css/style.css"></head><body>' + article.description + "</body></html>";
|
||||
|
||||
// calculate height to fill screen
|
||||
document.getElementById("rssDescription").style.height =
|
||||
"calc(100% - " + document.getElementById("rssTorrentDetailsName").offsetHeight + "px - "
|
||||
+ document.getElementById("rssTorrentDetailsDate").offsetHeight + "px - 5px)";
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -113,7 +113,7 @@
|
|||
<span></span>
|
||||
</div>
|
||||
|
||||
<div id="searchResultsNoSearches" style="display: none">
|
||||
<div id="searchResultsNoSearches" class="invisible">
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
|
@ -126,13 +126,13 @@
|
|||
<span></span>
|
||||
</div>
|
||||
|
||||
<div id="searchTabsToolbar" class="toolbarTabs" style="border-bottom: 1px solid var(--color-border-default); display: none">
|
||||
<div id="searchTabsToolbar" class="toolbarTabs invisible" style="border-bottom: 1px solid var(--color-border-default);">
|
||||
<ul id="searchTabs" class="tab-menu"></ul>
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
|
||||
<div id="searchResultsFilters" style="display: none;">
|
||||
<input type="text" id="searchInNameFilter" placeholder="QBT_TR(Filter)QBT_TR[CONTEXT=SearchEngineWidget]" autocorrect="off" autocapitalize="none">
|
||||
<div id="searchResultsFilters" class="invisible">
|
||||
<input type="text" id="searchInNameFilter" placeholder="QBT_TR(Filter)QBT_TR[CONTEXT=SearchEngineWidget]" aria-label="QBT_TR(Filter)QBT_TR[CONTEXT=SearchEngineWidget]" autocorrect="off" autocapitalize="none">
|
||||
|
||||
<span>QBT_TR(Results)QBT_TR[CONTEXT=SearchEngineWidget] (QBT_TR(showing)QBT_TR[CONTEXT=SearchEngineWidget] <span id="numSearchResultsVisible" class="numSearchResults">0</span> QBT_TR(out of)QBT_TR[CONTEXT=SearchEngineWidget] <span id="numSearchResultsTotal" class="numSearchResults">0</span>):</span>
|
||||
|
||||
|
@ -146,14 +146,14 @@
|
|||
<img id="searchResultsGranularFiltersWarning" src="images/dialog-warning.svg" title="QBT_TR(Increase window width to display additional filters)QBT_TR[CONTEXT=SearchEngineWidget]" alt="QBT_TR(Warning)QBT_TR[CONTEXT=SearchEngineWidget]" width="24" height="24">
|
||||
|
||||
<div id="searchResultsGranularFilters">
|
||||
<span style="margin-left: 15px;">QBT_TR(Seeds:)QBT_TR[CONTEXT=SearchEngineWidget]</span>
|
||||
<label for="searchMinSeedsFilter" style="margin-left: 15px;">QBT_TR(Seeds:)QBT_TR[CONTEXT=SearchEngineWidget]</label>
|
||||
<input type="number" min="0" max="1000" id="searchMinSeedsFilter" value="0" onchange="qBittorrent.Search.searchSeedsFilterChanged()">
|
||||
<span>QBT_TR(to)QBT_TR[CONTEXT=SearchEngineWidget]</span>
|
||||
<label for="searchMaxSeedsFilter">QBT_TR(to)QBT_TR[CONTEXT=SearchEngineWidget]</label>
|
||||
<input type="number" min="0" max="1000" id="searchMaxSeedsFilter" value="0" onchange="qBittorrent.Search.searchSeedsFilterChanged()">
|
||||
|
||||
<span style="margin-left: 15px;">QBT_TR(Size:)QBT_TR[CONTEXT=SearchEngineWidget]</span>
|
||||
<label for="searchMinSizeFilter" style="margin-left: 15px;">QBT_TR(Size:)QBT_TR[CONTEXT=SearchEngineWidget]</label>
|
||||
<input type="number" min="0" max="1000" step=".01" value="0.00" id="searchMinSizeFilter" onchange="qBittorrent.Search.searchSizeFilterChanged()">
|
||||
<select id="searchMinSizePrefix" onchange="qBittorrent.Search.searchSizeFilterPrefixChanged()">
|
||||
<select id="searchMinSizePrefix" onchange="qBittorrent.Search.searchSizeFilterPrefixChanged()" aria-label="QBT_TR(Min size prefix)QBT_TR[CONTEXT=SearchEngineWidget]">
|
||||
<option value="0">QBT_TR(B)QBT_TR[CONTEXT=misc]</option>
|
||||
<option value="1">QBT_TR(KiB)QBT_TR[CONTEXT=misc]</option>
|
||||
<option value="2" selected>QBT_TR(MiB)QBT_TR[CONTEXT=misc]</option>
|
||||
|
@ -162,9 +162,9 @@
|
|||
<option value="5">QBT_TR(PiB)QBT_TR[CONTEXT=misc]</option>
|
||||
<option value="6">QBT_TR(EiB)QBT_TR[CONTEXT=misc]</option>
|
||||
</select>
|
||||
<span>QBT_TR(to)QBT_TR[CONTEXT=SearchEngineWidget]</span>
|
||||
<label for="searchMaxSizeFilter">QBT_TR(to)QBT_TR[CONTEXT=SearchEngineWidget]</label>
|
||||
<input type="number" min="0" max="1000" step=".01" value="0.00" id="searchMaxSizeFilter" onchange="qBittorrent.Search.searchSizeFilterChanged()">
|
||||
<select id="searchMaxSizePrefix" onchange="qBittorrent.Search.searchSizeFilterPrefixChanged()">
|
||||
<select id="searchMaxSizePrefix" onchange="qBittorrent.Search.searchSizeFilterPrefixChanged()" aria-label="QBT_TR(Max size prefix)QBT_TR[CONTEXT=SearchEngineWidget]">
|
||||
<option value="0">QBT_TR(B)QBT_TR[CONTEXT=misc]</option>
|
||||
<option value="1">QBT_TR(KiB)QBT_TR[CONTEXT=misc]</option>
|
||||
<option value="2">QBT_TR(MiB)QBT_TR[CONTEXT=misc]</option>
|
||||
|
@ -177,7 +177,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div id="searchResultsTableContainer" style="display: none">
|
||||
<div id="searchResultsTableContainer" class="invisible">
|
||||
<div id="searchResultsTableFixedHeaderDiv" class="dynamicTableFixedHeaderDiv">
|
||||
<table class="dynamicTable unselectable" style="position:relative;">
|
||||
<thead>
|
||||
|
|
Loading…
Reference in a new issue