Skip to content

Commit ef892b4

Browse files
committed
fix: horizontal scrollbar
Fixe dissue #3969
1 parent 8bd7491 commit ef892b4

2 files changed

Lines changed: 112 additions & 7 deletions

File tree

studio/src/main/resources/static/css/studio.css

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -766,17 +766,33 @@ div.dt-search {
766766
margin-left: auto;
767767
}
768768

769-
/* Allow export dropdown to overflow; dataTables wrapper handles scrolling */
769+
/* Allow export dropdown to overflow (it uses position:fixed so it is safe);
770+
the horizontal scroll lives on the DataTables container / table-responsive. */
770771
#tab-table .table-responsive {
771-
overflow: visible;
772+
overflow-x: auto;
773+
overflow-y: visible;
774+
}
775+
776+
/* Force the tab-pane and its containers to honour parent width so a wide
777+
table scrolls horizontally inside its container instead of pushing the
778+
whole page wider. Without `min-width: 0`, the BS5 flex column grows to
779+
fit the table (intrinsic min-width) and the page itself scrolls. */
780+
#tab-table,
781+
#tab-table .table-responsive,
782+
#tab-table .dataTables_wrapper,
783+
#tab-table .dt-container {
784+
min-width: 0;
785+
max-width: 100%;
772786
}
773787

774-
#tab-table .dataTables_wrapper {
788+
#tab-table .dataTables_wrapper,
789+
#tab-table .dt-container {
775790
overflow-x: auto;
776791
}
777792

778793
/* Ensure footer stretches to table width when table overflows */
779-
#tab-table .dataTables_wrapper > .dt-layout-row:last-child {
794+
#tab-table .dataTables_wrapper > .dt-layout-row:last-child,
795+
#tab-table .dt-container > .dt-layout-row:last-child {
780796
width: 100%;
781797
min-width: fit-content;
782798
}
@@ -788,6 +804,7 @@ div.dt-search {
788804
color: var(--text-muted);
789805
padding: 8px 10px;
790806
border-bottom: 2px solid var(--border-table);
807+
position: relative;
791808
}
792809

793810
#tab-table .table tbody td {
@@ -796,6 +813,38 @@ div.dt-search {
796813
vertical-align: top;
797814
}
798815

816+
/* Column resize grip on the right edge of each header cell */
817+
#tab-table .table thead th .col-resizer {
818+
position: absolute;
819+
top: 0;
820+
right: 0;
821+
width: 6px;
822+
height: 100%;
823+
cursor: col-resize;
824+
user-select: none;
825+
z-index: 2;
826+
background: transparent;
827+
}
828+
829+
#tab-table .table thead th .col-resizer:hover,
830+
#tab-table .table thead th .col-resizer.resizing {
831+
background: var(--accent, #0d6efd);
832+
opacity: 0.35;
833+
}
834+
835+
body.col-resizing,
836+
body.col-resizing * {
837+
cursor: col-resize !important;
838+
user-select: none !important;
839+
}
840+
841+
/* When columns have explicit widths, ensure cells don't overflow horizontally */
842+
#tab-table .table.col-resized tbody td,
843+
#tab-table .table.col-resized thead th {
844+
overflow: hidden;
845+
text-overflow: ellipsis;
846+
}
847+
799848
#tab-table .table tbody tr:hover {
800849
background-color: var(--table-hover);
801850
}

studio/src/main/resources/static/js/studio-table.js

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ function renderTable() {
2121
if (tableFitInPageChecked == true) tableFitInPageChecked = "checked";
2222
else tableFitInPageChecked = "";
2323

24-
if (tableFitInPage == true) $("#result").css({ width: "100%", "table-layout": "fixed", "white-space": "normal" });
25-
else $("#result").css({ width: "100%", "table-layout": "auto", "white-space": "nowrap" });
24+
if (tableFitInPage == true) $("#result").css({ width: "100%", "min-width": "", "table-layout": "fixed", "white-space": "normal" });
25+
else $("#result").css({ width: "max-content", "min-width": "100%", "table-layout": "auto", "white-space": "nowrap" });
2626

2727
let tablePageLength = parseInt(globalStorageLoad("table.pageLength"));
2828
if (isNaN(tablePageLength)) tablePageLength = 20;
@@ -112,13 +112,14 @@ function renderTable() {
112112
if (globalResultset.records.length > 0) {
113113
const dataTable = $("#result").DataTable({
114114
orderCellsTop: true,
115-
fixedHeader: true,
115+
fixedHeader: tableFitInPage,
116116
paging: true,
117117
pageLength: tablePageLength,
118118
lengthChange: true,
119119
columns: tableColumns,
120120
data: tableRecords,
121121
deferRender: true,
122+
autoWidth: tableFitInPage,
122123
dom: "<Blf>rt<ip>",
123124
order: [],
124125
lengthMenu: [
@@ -162,6 +163,8 @@ function renderTable() {
162163
else
163164
wrapper.prepend(exportEl);
164165
}, 50);
166+
167+
attachColumnResizers($("#result"));
165168
},
166169
});
167170

@@ -177,6 +180,59 @@ function renderTable() {
177180
$("#result_filter>label>input").val("");
178181
}
179182

183+
// Adds a drag handle on the right edge of every header cell so the user can
184+
// resize individual columns. When the first column is resized the table is
185+
// switched to `table-layout: fixed` so further column widths are stable.
186+
function attachColumnResizers($table) {
187+
if (!$table || !$table.length) return;
188+
189+
$table.find("thead th").each(function () {
190+
let $th = $(this);
191+
if ($th.find(".col-resizer").length) return;
192+
193+
let $grip = $("<span class='col-resizer' title='Drag to resize column'></span>");
194+
$th.append($grip);
195+
196+
$grip.on("mousedown", function (e) {
197+
e.preventDefault();
198+
e.stopPropagation();
199+
200+
let startX = e.pageX;
201+
let startWidth = $th.outerWidth();
202+
203+
// Freeze every column at its current width so only the resized one moves.
204+
if (!$table.hasClass("col-resized")) {
205+
$table.find("thead th").each(function () {
206+
let $c = $(this);
207+
let w = $c.outerWidth();
208+
$c.css({ width: w + "px", "min-width": w + "px" });
209+
});
210+
$table.css("table-layout", "fixed").addClass("col-resized");
211+
}
212+
213+
$grip.addClass("resizing");
214+
$("body").addClass("col-resizing");
215+
216+
$(document).on("mousemove.colresize", function (ev) {
217+
let delta = ev.pageX - startX;
218+
let newW = Math.max(40, startWidth + delta);
219+
$th.css({ width: newW + "px", "min-width": newW + "px" });
220+
});
221+
222+
$(document).on("mouseup.colresize", function () {
223+
$(document).off("mousemove.colresize mouseup.colresize");
224+
$grip.removeClass("resizing");
225+
$("body").removeClass("col-resizing");
226+
});
227+
});
228+
229+
// Prevent the click from triggering DataTables column sort.
230+
$grip.on("click", function (e) {
231+
e.stopPropagation();
232+
});
233+
});
234+
}
235+
180236
function toggleExportMenu(btn) {
181237
let menu = $(btn).siblings(".dt-export-menu");
182238
let isOpen = menu.hasClass("open");

0 commit comments

Comments
 (0)