From 8add0bf5ec381e5a5280afec379f78d7f386f05c Mon Sep 17 00:00:00 2001 From: "stefanorigano (SRI)" Date: Wed, 24 Aug 2022 10:55:19 +0000 Subject: [PATCH] [IMP] web: `ListRenderer` improve design, simplify scss Part of the overall v16 SCSS optimization/restyle, task-2704984. task-2946835 Part-of: odoo/odoo#98234 --- .../src/legacy/scss/bootstrap_overridden.scss | 6 +- addons/web/static/src/legacy/scss/fields.scss | 1 + .../src/views/list/list_controller.scss | 186 +++++++++--------- .../static/src/views/list/list_renderer.js | 63 +++++- .../static/src/views/list/list_renderer.xml | 50 +++-- .../src/views/list/list_view_extra.scss | 4 - .../web/static/tests/views/list_view_tests.js | 16 +- 7 files changed, 198 insertions(+), 128 deletions(-) diff --git a/addons/web/static/src/legacy/scss/bootstrap_overridden.scss b/addons/web/static/src/legacy/scss/bootstrap_overridden.scss index 61048542c6f..a572e6e5061 100644 --- a/addons/web/static/src/legacy/scss/bootstrap_overridden.scss +++ b/addons/web/static/src/legacy/scss/bootstrap_overridden.scss @@ -132,7 +132,7 @@ $headings-color: $o-main-headings-color !default; $table-striped-color: inherit !default; $table-striped-bg-factor: .01 !default; -$table-hover-bg: rgba($black, .04) !default; +$table-hover-bg-factor: .075 !default; $table-border-color: $gray-200 !default; $table-group-separator-color: $gray-200 !default; $table-cell-padding-x: .75rem !default; @@ -140,6 +140,10 @@ $table-cell-padding-y: .75rem !default; $table-cell-padding-x-sm: .3rem !default; $table-cell-padding-y-sm: .3rem !default; +$table-active-bg-factor: .05 !default; +$table-active-color: $headings-color !default; +$table-th-font-weight: $headings-font-weight !default; + // Buttons // $btn-border-radius-lg: $border-radius !default; diff --git a/addons/web/static/src/legacy/scss/fields.scss b/addons/web/static/src/legacy/scss/fields.scss index 23fdbebf101..b1b8c47c4d0 100644 --- a/addons/web/static/src/legacy/scss/fields.scss +++ b/addons/web/static/src/legacy/scss/fields.scss @@ -30,6 +30,7 @@ .o_input { border:$input-border-width solid var(--o-input-border-color); padding: $o-input-padding-y $o-input-padding-x; + background-color: var(--o-input-background-color, unset); // -- Nested o_input(s) .o_input { diff --git a/addons/web/static/src/views/list/list_controller.scss b/addons/web/static/src/views/list/list_controller.scss index 07739a09189..165055bb215 100644 --- a/addons/web/static/src/views/list/list_controller.scss +++ b/addons/web/static/src/views/list/list_controller.scss @@ -1,32 +1,52 @@ .o_list_renderer { - position: relative; - height: 100%; + --ListRenderer-table-padding-h: #{$table-cell-padding-x-sm}; + + --ListRenderer-thead-padding-v: #{$table-cell-padding-y * 1.25}; + --ListRenderer-thead-bg-color: #{$gray-200}; + --ListRenderer-thead-border-end-color: #{$border-color}; + + .o_list_view & { + --ListRenderer-table-padding-h: #{$o-horizontal-padding}; + } .o_list_table { - position: relative; - cursor: pointer; background-color: $o-view-background-color; - margin-bottom: 0px; - // Don't understand why but without those, the list view appears broken - // in both form views and standalone list views. - border-collapse: initial; - border-spacing: 0; + > thead, > tbody, > tfoot { + > tr > :first-child { + padding-left: var(--ListRenderer-table-padding-h); + } - thead { - color: $o-main-headings-color; - border-bottom: 1px solid #cacaca; - > tr > th:not(.o_list_record_selector) { - border-left: 1px solid #dfdfdf; - @include o-text-overflow(table-cell); - &:not(.o_column_sortable):hover { - cursor: default; - } - &.o_list_number_th { - text-align: right; - } + > tr > :last-child { + padding-right: var(--ListRenderer-table-padding-h); + } + + > tr > :empty { + padding: 0; } } + + thead, tfoot { + tr > :not(:empty) { + padding-top: var(--ListRenderer-thead-padding-v); + padding-bottom: var(--ListRenderer-thead-padding-v); + } + } + + thead { + background-color: var(--ListRenderer-thead-bg-color); + color: $headings-color; + + th:not(.o_list_record_selector) { + border-left: $border-width solid var(--ListRenderer-thead-border-end-color); + } + } + + // Override BS default + > :not(:first-child) { + border-top-width: 0; + } + tbody > tr > td:not(.o_list_record_selector) { &:not(.o_handle_cell):not(.o_list_button):not(.o_state_selection_cell) { @include o-text-overflow(table-cell); @@ -68,72 +88,32 @@ } tfoot { - cursor: default; + // cursor: default; color: $o-list-footer-color; background-color: $o-list-footer-bg-color; font-weight: $o-list-footer-font-weight; - border-top: 2px solid #cacaca; - border-bottom: 1px solid #cacaca; + border-top: 1px solid $table-border-color; + border-bottom: 1px solid $table-border-color; + > tr > td { @include o-text-overflow(table-cell); } - .o_list_number { - text-align: right; - } } .o_column_sortable:not(.o_handle_cell) { - position: relative; user-select: none; // Prevent unwanted selection while sorting - - &::after { - font-family: FontAwesome; - content: "\f0d7"; - opacity: 0; - } - &:not(:empty)::after { - margin-left: 6px; - } - &.o-sort-up { - cursor: n-resize; - &::after { - content: "\f0d7"; - } - } - &.o-sort-down { - cursor: s-resize; - &::after { - content: "\f0d8"; - } - } - &:hover::after { - opacity: 0.3; - } - &.o-sort-up, &.o-sort-down { - color: $headings-color; - &::after { - opacity: 1; - } - } } - .o_list_record_selector { - width: 40px; - padding-left: $o-horizontal-padding; - .o-checkbox { - margin-bottom: $form-check-margin-bottom; - } - .form-check-label { - cursor: pointer; - } - } .o_list_record_remove, .o_handle_cell { width: 1px; // to prevent the column to expand } - .o_list_record_selector, .o_handle_cell { - max-width: 35px !important; + + .o_list_table_ungrouped { + .o_list_controller { + max-width: 35px !important; + } } .o_list_record_remove button { @@ -144,6 +124,42 @@ cursor: pointer; } + tbody tr { + outline-offset: -1px; + + &:hover { + outline: 1px solid $gray-400; + } + + &.table-info { + outline-color: $info; + } + + &.o_selected_row { + th:focus-within, td:focus-within { + --table-accent-bg: none; + + background-color: $o-view-background-color; + outline-offset: -1px; + outline: 1px solid $primary; + } + } + } + + .o_keyboard_navigation { + tr:hover { + outline-color: transparent; + } + + th:focus-within, td:focus-within { + --table-accent-bg: none; + + background-color: $o-view-background-color; + outline-offset: -1px; + outline: 1px solid $primary; + } + } + // Grouped list views tbody > tr.o_group_header { > th, > td { @@ -189,16 +205,7 @@ border-top: none; // Override bootstrap for grouped list views } - &, &.table-striped { - tr:focus-within { - background-color: lighten($o-form-lightsecondary, 10%); - } - } - thead th:focus-within, - tbody.o_keyboard_navigation td:focus-within { - background-color: $o-form-lightsecondary; - outline: none; - } + .o_data_cell.o_text_overflow, .o_data_cell.o_invisible_modifier { @@ -220,13 +227,7 @@ } span.o_resize { - bottom: 0; cursor: col-resize; - position: absolute; - right: 0; - top: 0; - width: 10px; - z-index: 1; // must be over the sort caret (::after pseudo-element) } .o_list_actions_header { @@ -274,10 +275,7 @@ } .o_data_row.o_selected_row > .o_data_cell { - position: relative; // for o_field_translate - &:not(.o_handle_cell) { - background-color: white; - } + > .o_field_widget:not(.o_readonly_modifier):not(.o_invisible_modifier) { width: 100%; .o_input { @@ -306,6 +304,9 @@ // reduce basis to prevent many2one input from going to the next line to often flex-basis: 40px; } + .o_field_many2one_avatar { + display: flex; + } &.o_field_badge { width: auto; // override width: 100% to keep the optimal badge width } @@ -349,8 +350,3 @@ } } } - -.o_list_view.o_view_controller > .o_content > .table-responsive { - // in main list views, let .o_content scroll - overflow: visible; -} diff --git a/addons/web/static/src/views/list/list_renderer.js b/addons/web/static/src/views/list/list_renderer.js index 5366f63d62b..03348a23965 100644 --- a/addons/web/static/src/views/list/list_renderer.js +++ b/addons/web/static/src/views/list/list_renderer.js @@ -562,13 +562,15 @@ export class ListRenderer extends Component { const field = this.fields[column.name]; const classNames = ["align-middle"]; if (field.sortable && column.hasLabel) { - classNames.push("o_column_sortable"); + classNames.push("o_column_sortable", "position-relative", "cursor-pointer"); + } else { + classNames.push("cursor-default"); } const orderBy = this.props.list.orderBy; - if (orderBy.length && orderBy[0].name === column.name) { - classNames.push(orderBy[0].asc ? "o-sort-up" : "o-sort-down"); + if (orderBy.length && column.widget !== "handle" && orderBy[0].name === column.name) { + classNames.push("table-active"); } - if (["float", "integer", "monetary"].includes(field.type)) { + if (this.isNumericColumn(column)) { classNames.push("o_list_number_th"); } if (column.type === "button_group") { @@ -592,6 +594,24 @@ export class ListRenderer extends Component { return this.state.columns; } + isNumericColumn(column) { + const { type } = this.fields[column.name]; + return ["float", "integer", "monetary"].includes(type); + } + + getSortableIconClass(column) { + const { sortable } = this.fields[column.name]; + const { orderBy } = this.props.list; + const classNames = sortable && column.hasLabel ? ["fa", "fa-lg", "px-2"] : ["d-none"]; + if (orderBy.length && orderBy[0].name === column.name) { + classNames.push(orderBy[0].asc ? "fa-angle-up" : "fa-angle-down"); + } else { + classNames.push("fa-angle-down", "opacity-0", "opacity-75-hover"); + } + + return classNames.join(" "); + } + /** * Returns the classnames to apply to the row representing the given record. * @param {Record} record @@ -602,6 +622,9 @@ export class ListRenderer extends Component { const classNames = this.props.archInfo.decorations .filter((decoration) => evaluateExpr(decoration.condition, record.evalContext)) .map((decoration) => decoration.class); + if (record.selected) { + classNames.push("table-info"); + } // "o_selected_row" classname for the potential row in edition if (record.isInEdition) { classNames.push("o_selected_row"); @@ -661,6 +684,15 @@ export class ListRenderer extends Component { } } } + if ( + record.isInEdition && + this.props.list.editedRecord && + this.props.list.editedRecord.isReadonly(column.name) + ) { + classNames.push("pe-none", "text-muted"); + } else { + classNames.push("cursor-pointer"); + } } return classNames.join(" "); } @@ -1592,6 +1624,24 @@ export class ListRenderer extends Component { }); } + /** + * Handles the :hover effect on sortable column headers + * + * @private + * @param {MouseEvent} ev + */ + onHoverSortColumn(ev, column) { + if (this.props.list.orderBy.length && this.props.list.orderBy[0].name === column.name) { + return; + } else if ( + this.fields[column.name].sortable && + column.widget !== "handle" && + column.hasLabel + ) { + ev.target.classList.toggle("table-active", ev.type == "mouseenter"); + } + } + /** * Handles the resize feature on the column headers * @@ -1601,6 +1651,7 @@ export class ListRenderer extends Component { onStartResize(ev) { const table = this.tableRef.el; const th = ev.target.closest("th"); + const handler = th.querySelector(".o_resize"); table.style.width = `${Math.floor(table.getBoundingClientRect().width)}px`; const thPosition = [...th.parentNode.children].indexOf(th); const resizingColumnElements = [...table.getElementsByTagName("tr")] @@ -1622,6 +1673,8 @@ export class ListRenderer extends Component { table.classList.add("o_resizing"); for (const el of resizingColumnElements) { el.classList.add("o_column_resizing"); + handler.classList.add("bg-primary", "opacity-100"); + handler.classList.remove("bg-black-25", "opacity-50-hover"); } // Mousemove event : resize header const resizeHeader = (ev) => { @@ -1648,6 +1701,8 @@ export class ListRenderer extends Component { table.classList.remove("o_resizing"); for (const el of resizingColumnElements) { el.classList.remove("o_column_resizing"); + handler.classList.remove("bg-primary", "opacity-100"); + handler.classList.add("bg-black-25", "opacity-50-hover"); } window.removeEventListener("mousemove", resizeHeader); diff --git a/addons/web/static/src/views/list/list_renderer.xml b/addons/web/static/src/views/list/list_renderer.xml index ffa4cb0286e..3d63ea443ff 100644 --- a/addons/web/static/src/views/list/list_renderer.xml +++ b/addons/web/static/src/views/list/list_renderer.xml @@ -3,7 +3,7 @@
- +
- - + -
- + + + - - + +
+ + +
+
- + -
- + @@ -142,11 +158,15 @@ -
- + + ()
@@ -197,7 +217,7 @@
- +