diff options
| author | Julien Mougenot <jum@odoo.com> | 2019-09-20 14:29:17 +0000 |
|---|---|---|
| committer | Aaron Bohy <aab@odoo.com> | 2019-09-25 06:16:54 +0000 |
| commit | 0ebacff465e35dc8465560bddb8ac7ad911bf2cc (patch) | |
| tree | 0482bfe436dbfae486d688bd79cf2e698e88824b /addons/web/static/src | |
| parent | d99e80f92a6f8d940958c6bdb652b2ce4e1deea4 (diff) | |
[FIX] web: list: prevent tables from overflowing
In an editable list, select a very long value in a many2one...
Before this commit, the table stretched as much as it needed to to
display all of the columns content.
Now, the largest columns are trimmed in order to keep the table
as wide as the list container (meaning it won't overflow anymore
unless there is a huge load of columns).
Also ensured that values trimmed by this feature are followed
by an ellipsis.
Task 2068261
Co-authored-by: Aaron Bohy <aab@odoo.com>
Diffstat (limited to 'addons/web/static/src')
| -rw-r--r-- | addons/web/static/src/js/views/list/list_editable_renderer.js | 99 | ||||
| -rw-r--r-- | addons/web/static/src/scss/list_view.scss | 15 |
2 files changed, 88 insertions, 26 deletions
diff --git a/addons/web/static/src/js/views/list/list_editable_renderer.js b/addons/web/static/src/js/views/list/list_editable_renderer.js index a134faccc81..c9fd1cea92b 100644 --- a/addons/web/static/src/js/views/list/list_editable_renderer.js +++ b/addons/web/static/src/js/views/list/list_editable_renderer.js @@ -411,18 +411,6 @@ ListRenderer.include({ var $tds = $row.children('.o_data_cell'); var oldWidgets = _.clone(this.allFieldWidgets[record.id]); - // When switching to edit mode, force the dimensions of all cells to - // their current value so that they won't change if their content - // changes, to prevent the view from flickering. - // We need to use getBoundingClientRect instead of outerWidth to - // prevent a rounding issue on Firefox. - if (editMode) { - $tds.each(function () { - var $td = $(this); - $td.css({width: $td[0].getBoundingClientRect().width}); - }); - } - // Prepare options for cell rendering (this depends on the mode) var options = { renderInvisible: editMode, @@ -608,19 +596,25 @@ ListRenderer.include({ } const table = this.el.getElementsByTagName('table')[0]; const thead = table.getElementsByTagName('thead')[0]; + const thElements = [...thead.getElementsByTagName('th')]; - // Set table layout auto and remove inline style to make sure that css - // rules apply (e.g. fixed width of record selector) - table.style.tableLayout = 'auto'; - [...table.getElementsByTagName('th')].forEach((th) => { - th.style.width = null; - }); + let columnWidths = this.columnWidths; + if (!columnWidths) { // no column widths to restore + // Set table layout auto and remove inline style to make sure that css + // rules apply (e.g. fixed width of record selector) + table.style.tableLayout = 'auto'; + thElements.forEach((th) => { + th.style.width = null; + th.style.maxWidth = null; + }); + + // Squeeze the table by applying a max-width on largest columns to + // ensure that it doesn't overflow + columnWidths = this._squeezeTable(); + } - // Freeze each th width according to their size in auto layout - const thElements = [...thead.getElementsByTagName('th')]; - const thWidths = thElements.map((th) => th.offsetWidth); thElements.forEach((th, index) => { - th.style.width = `${this.columnWidths ? this.columnWidths[index] : thWidths[index]}px`; + th.style.width = `${columnWidths[index]}px`; }); // Set the table layout to fixed @@ -1257,6 +1251,67 @@ ListRenderer.include({ }); }, /** + * Set a maximum width on the largest columns in the list in case the table + * is overflowing. The idea is to shrink largest columns first, but to + * ensure that they are still the largest at the end (maybe in equal measure + * with other columns). + * + * @private + * @returns {integer[]} width (in px) of each column s.t. the table doesn't + * overflow + */ + _squeezeTable: function () { + const table = this.el.getElementsByTagName('table')[0]; + const thead = table.getElementsByTagName('thead')[0]; + const thElements = [...thead.getElementsByTagName('th')]; + const columnWidths = thElements.map(th => th.offsetWidth); + const getWidth = th => columnWidths[thElements.indexOf(th)] || 0; + const getTotalWidth = () => thElements.reduce((tot, th, i) => tot + columnWidths[i], 0); + const shrinkColumns = (columns, width) => { + let thresholdReached = false; + columns.forEach(th => { + const index = thElements.indexOf(th); + let maxWidth = columnWidths[index] - Math.ceil(width / columns.length); + if (maxWidth < 92) { // prevent the columns from shrinking under 92px (~ date field) + maxWidth = 92; + thresholdReached = true; + } + th.style.maxWidth = `${maxWidth}px`; + columnWidths[index] = maxWidth; + }); + return thresholdReached; + }; + // Sort columns, largest first + const sortedThs = [...thead.getElementsByTagName('th')] + .sort((a, b) => getWidth(b) - getWidth(a)); + const allowedWidth = table.parentNode.offsetWidth; + + let totalWidth = getTotalWidth(); + let stop = false; + let index = 0; + while (totalWidth > allowedWidth && !stop) { + // Find the largest columns + index++; + const largests = sortedThs.slice(0, index); + while (getWidth(largests[0]) === getWidth(sortedThs[index])) { + largests.push(sortedThs[index]); + index++; + } + + // Compute the number of px to remove from the largest columns + const nextLargest = sortedThs[index]; // largest column when omitting those in largests + const totalToRemove = totalWidth - allowedWidth; + const canRemove = (getWidth(largests[0]) - getWidth(nextLargest)) * largests.length; + + // Shrink the largests columns + stop = shrinkColumns(largests, Math.min(totalToRemove, canRemove)); + + totalWidth = getTotalWidth(); + } + + return columnWidths; + }, + /** * @private */ _storeColumnWidths: function () { diff --git a/addons/web/static/src/scss/list_view.scss b/addons/web/static/src/scss/list_view.scss index 6c3d452fb47..4ee916f9a1f 100644 --- a/addons/web/static/src/scss/list_view.scss +++ b/addons/web/static/src/scss/list_view.scss @@ -211,10 +211,6 @@ .o_list_record_remove_header { width: 32px; } - &>tbody>tr>td.o_list_char, - &>tbody>tr>td.o_list_many2one { - white-space: nowrap; - } // checkboxes in editable lists should be clickable and activate the row .o_data_row:not(.o_selected_row) .o_data_cell { &.o_toggle_button_cell:not(.o_readonly_modifier) button, // registry: toggle_button @@ -224,6 +220,17 @@ } } + .o_data_row:not(.o_selected_row) { + .o_list_many2one, + .o_list_char, + .o_list_number { + white-space: nowrap; + .o_field_widget { + display: inline; + } + } + } + .o_data_row.o_selected_row > .o_data_cell:not(.o_readonly_modifier):not(.o_invisible_modifier) { position: relative; // for o_field_translate background-color: white; |
