diff options
| author | Géry Debongnie <ged@odoo.com> | 2019-08-09 08:07:55 +0000 |
|---|---|---|
| committer | Aaron Bohy <aab@odoo.com> | 2019-08-23 06:28:34 +0000 |
| commit | bf3c1d3e0c2cb9f6ed560994d24de026c080aa01 (patch) | |
| tree | c0f38a904ad368b62276d519d53fbc9aee48811e /addons/web/static/src/js | |
| parent | 43ad74798fd971e2c514b67a0f706b8e65165843 (diff) | |
[IMP] web: editable list: column's widths
This rev. refines the way we compute column's widths in editable
list views.
When there are displayed records, we let the browser compute the
width of each column (which is thus optimal with respect to the
content), and we then freeze those widths s.t. it doesn't flicker
when we switch a row in edition.
When there is no record, we keep the former heuristic based on
absolute and relative widths (depending on field types). However,
we set the same weight for all fields having a relative width (i.e.
all but boolean, date(time) and numeric), such that the remaining
space is evenly distributed between them.
Co-authored-by: Aaron Bohy <aab@odoo.com>
Diffstat (limited to 'addons/web/static/src/js')
3 files changed, 97 insertions, 17 deletions
diff --git a/addons/web/static/src/js/fields/relational_fields.js b/addons/web/static/src/js/fields/relational_fields.js index fcae2043d00..eefb771c69a 100644 --- a/addons/web/static/src/js/fields/relational_fields.js +++ b/addons/web/static/src/js/fields/relational_fields.js @@ -17,10 +17,11 @@ var AbstractField = require('web.AbstractField'); var basicFields = require('web.basic_fields'); var concurrency = require('web.concurrency'); var ControlPanelView = require('web.ControlPanelView'); -var dialogs = require('web.view_dialogs'); var core = require('web.core'); var data = require('web.data'); var Dialog = require('web.Dialog'); +var dialogs = require('web.view_dialogs'); +var dom = require('web.dom'); var KanbanRecord = require('web.KanbanRecord'); var KanbanRenderer = require('web.KanbanRenderer'); var ListRenderer = require('web.ListRenderer'); @@ -905,6 +906,22 @@ var FieldX2Many = AbstractField.extend({ start: function () { return this._renderControlPanel().then(this._super.bind(this)); }, + /** + * For the list renderer to properly work, it must know if it is in the DOM, + * and be notified when it is attached to the DOM. + */ + on_attach_callback: function () { + this.isInDOM = true; + if (this.renderer) { + this.renderer.on_attach_callback(); + } + }, + /** + * For the list renderer to properly work, it must know if it is in the DOM. + */ + on_detach_callback: function () { + this.isInDOM = false; + }, //-------------------------------------------------------------------------- // Public @@ -1096,7 +1113,16 @@ var FieldX2Many = AbstractField.extend({ this.renderer = new Renderer(this, this.value, rendererParams); this.$el.addClass('o_field_x2many o_field_x2many_' + viewType); - return this.renderer ? this.renderer.appendTo(this.$el) : this._super(); + if (this.renderer) { + return this.renderer.appendTo(document.createDocumentFragment()).then(function () { + dom.append(self.$el, self.renderer.$el, { + in_DOM: self.isInDOM, + callbacks: [{widget: self.renderer}], + }); + }); + } else { + return this._super(); + } }, /** * Instanciates a control panel with the appropriate buttons and a pager. 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 65f5b433c27..9275d414578 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 @@ -43,6 +43,8 @@ ListRenderer.include({ var self = this; this._super.apply(this, arguments); + this.editable = params.editable; + // if addCreateLine (resp. addCreateLineInGroups) is true, the renderer // will add a 'Add a line' link at the bottom of the list view (resp. // at the bottom of each group) @@ -107,6 +109,29 @@ ListRenderer.include({ } return this._super(); }, + /** + * The list renderer needs to know if it is in the DOM, and to be notified + * when it is attached to the DOM to properly compute column widths. + * + * @override + */ + on_attach_callback: function () { + this.isInDOM = true; + if (this.editable) { + this._freezeColumnWidths(); + } + this._super(); + }, + /** + * The list renderer needs to know if it is in the DOM to properly compute + * column widths. + * + * @override + */ + on_detach_callback: function () { + this.isInDOM = false; + this._super(); + }, //-------------------------------------------------------------------------- // Public @@ -493,9 +518,39 @@ ListRenderer.include({ } }, /** + * This function freezes the column widths and forces a fixed table-layout, + * once the browser has computed the optimal width of each column according + * to the displayed records. We want to freeze widths s.t. it doesn't + * flicker when we switch a row in edition. + * + * We skip this when there is no record as we don't want to fix widths + * according to column's labels. In this case, we fallback on the 'weight' + * heuristic, which assigns to each column a fixed or relative width + * depending on the widget or field type. + * + * Note that the list must be in the DOM when this function is called. + * + * @private + */ + _freezeColumnWidths: function () { + if (!this.state.data.length) { + // there is no record -> don't force column's widths w.r.t. their label + return; + } + var $thead = this.$('thead'); + $thead.find('th').each(function () { + var $th = $(this); + $th.css('width', $th.outerWidth() + 'px'); + }); + this.$('table').css('table-layout', 'fixed'); + }, + /** * Returns the relative width according to the widget or the field type. - * @see _renderHeader + * This is only used when there is no record in the list (i.e. when we can't + * let the browser compute the optimal width of each column). * + * @see _renderHeader + * @private * @param {Object} column an arch node * @returns {integer | string} either a weight factor (number) or a css * width description (string) @@ -516,22 +571,13 @@ ListRenderer.include({ return widget.widthFactor; } switch (field.type) { - case 'binary': return 1; case 'boolean': return '40px'; - case 'char': return 2; case 'date': return '92px'; case 'datetime': return '146px'; case 'float': return '92px'; - case 'html': return 3; case 'integer': return '74px'; - case 'many2many': return 2; - case 'many2one': return 2; case 'monetary': return '104px'; - case 'one2many': return 2; - case 'reference': return 1.5; - case 'selection': return 1.5; - case 'text': return 2.5; - default: return 1; + default: return 1; // evenly distribute the remaining space } }, /** @@ -842,7 +888,7 @@ ListRenderer.include({ _renderHeader: function () { var $thead = this._super.apply(this, arguments); - if (this.editable) { + if (this.editable && !this.state.data.length) { // we compute the sum of the weights for each columns, excluding // those with a fixed width. var totalWidth = this.columns.reduce(function (acc, column) { @@ -934,8 +980,17 @@ ListRenderer.include({ this.currentRow = null; this.allRecordsIds = null; return this._super.apply(this, arguments).then(function () { - if (self.editable) { - self.$('table').addClass('o_editable_list'); + var table = self.el.getElementsByTagName('table')[0]; + if (table) { // no table if no content helper displayed + if (!self.state.data.length) { // FIXME: only handles ungrouped case + table.classList.add('o_empty_list'); + } + if (self.editable) { + table.classList.add('o_editable_list'); + if (self.isInDOM) { + self._freezeColumnWidths(); + } + } } }); }, diff --git a/addons/web/static/src/js/views/list/list_renderer.js b/addons/web/static/src/js/views/list/list_renderer.js index fda8d12f9e4..98c8bf71046 100644 --- a/addons/web/static/src/js/views/list/list_renderer.js +++ b/addons/web/static/src/js/views/list/list_renderer.js @@ -67,7 +67,6 @@ var ListRenderer = BasicRenderer.extend({ this.hasSelectors = params.hasSelectors; this.selection = params.selectedRecords || []; this.pagers = []; // instantiated pagers (only for grouped lists) - this.editable = params.editable; this.isGrouped = this.state.groupedBy.length > 0; this.groupbys = params.groupbys; }, |
