From 384ba04118e266c3e84f632b74f6d00186308a60 Mon Sep 17 00:00:00 2001 From: Jurgis Pralgauskis Date: Thu, 14 Mar 2024 10:21:15 +0200 Subject: [PATCH] Feature 22921 mig colorize field in tree views jpr (#4) [MIG] web_tree_dynamic_colored_field: Migration to 17.0 - Implementation now is based on updating `style` attribute of `cell/td` instead of directly manipulating element's CSS value. - Cleanup docs about no more implemented `colors` parameter for `tree` --- web_tree_dynamic_colored_field/README.rst | 33 +--- .../__manifest__.py | 5 +- .../readme/CONTRIBUTORS.md | 1 + .../readme/CREDITS.md | 1 + .../readme/ROADMAP.md | 1 + .../readme/USAGE.md | 28 +-- .../static/description/index.html | 34 +--- .../static/src/js/list_renderer.esm.js | 78 +++++++++ .../src/js/web_tree_dynamic_colored_field.js | 165 ------------------ .../static/src/xml/list.xml | 19 ++ 10 files changed, 119 insertions(+), 246 deletions(-) create mode 100644 web_tree_dynamic_colored_field/static/src/js/list_renderer.esm.js delete mode 100644 web_tree_dynamic_colored_field/static/src/js/web_tree_dynamic_colored_field.js create mode 100644 web_tree_dynamic_colored_field/static/src/xml/list.xml diff --git a/web_tree_dynamic_colored_field/README.rst b/web_tree_dynamic_colored_field/README.rst index b8b14d5a4cdf..7c6bcb052598 100644 --- a/web_tree_dynamic_colored_field/README.rst +++ b/web_tree_dynamic_colored_field/README.rst @@ -63,7 +63,7 @@ Usage ... - With this example, column which renders 'name' field will have its background colored in red. + With this example, column which renders 'name' field will have its **background** colored in red on customer records. - In the tree view declaration, put ``options='{"fg_color": "white:customer == True"}'`` attribute in the @@ -81,26 +81,7 @@ Usage ... - With this example, column which renders 'name' field will have its text colored in white on a customer records. - -- In the tree view declaration, use - ``options='"color_field": "my_color"'`` attribute in the ``tree`` - tag: - - :: - - ... - - - ... - - ... - - - ... - -- You can also use ``colors="bg_color_field: my_color"`` to defined the - field name that will be used for the background color of the line. + With this example, column which renders 'name' field will have its **text** colored in white on customer records. - If you want to use more than one color, you can split the attributes using ';': @@ -123,12 +104,6 @@ Example: ... - With this example, the content of the field named `my_color` will be used to - populate the `my_color` CSS value. Use a function field to return whichever - color you want depending on the other record values. Note that this - overrides the rest of `colors` attributes, and that you need the tree - to load your field in the first place by adding it as invisible field. - - Can use strings too... In the tree view declaration, put ``options="{'fg_color': 'green:customer_state == \'success\''}"`` attribute in the ``field`` tag: @@ -158,6 +133,8 @@ Known issues / Roadmap ``colors`` attribute is no longer in the RelaxNG schema of the tree view, so we can't use it anymore. This feature has then been dropped, but could be reimplement in another way. +- Since version 17.0 coloring is written into ``style`` attribute of + (td) element Bug Tracker =========== @@ -187,6 +164,7 @@ Contributors - Guewen Baconnier - Phuc Tran Thanh - Sylvain LE GAL +- Jurgis Pralgauskis Other credits ------------- @@ -194,6 +172,7 @@ Other credits The development of this module has been financially supported by: - Camptocamp +- Versada Maintainers ----------- diff --git a/web_tree_dynamic_colored_field/__manifest__.py b/web_tree_dynamic_colored_field/__manifest__.py index 62175de5a910..b1e99b51a90c 100644 --- a/web_tree_dynamic_colored_field/__manifest__.py +++ b/web_tree_dynamic_colored_field/__manifest__.py @@ -4,7 +4,7 @@ "name": "Colorize field in tree views", "summary": "Allows you to dynamically color fields on tree views", "category": "Hidden/Dependency", - "version": "15.0.1.0.1", + "version": "17.0.1.0.0", "depends": ["web"], "author": "Camptocamp, Therp BV, Odoo Community Association (OCA)", "license": "AGPL-3", @@ -13,7 +13,8 @@ "installable": True, "assets": { "web.assets_backend": [ - "/web_tree_dynamic_colored_field/static/src/js/web_tree_dynamic_colored_field.js", + "web_tree_dynamic_colored_field/static/src/xml/list.xml", + "web_tree_dynamic_colored_field/static/src/js/list_renderer.esm.js", ], }, } diff --git a/web_tree_dynamic_colored_field/readme/CONTRIBUTORS.md b/web_tree_dynamic_colored_field/readme/CONTRIBUTORS.md index 9a3eff1128cc..36093db6e17f 100644 --- a/web_tree_dynamic_colored_field/readme/CONTRIBUTORS.md +++ b/web_tree_dynamic_colored_field/readme/CONTRIBUTORS.md @@ -4,3 +4,4 @@ - Guewen Baconnier \<\> - Phuc Tran Thanh \<\> - Sylvain LE GAL \<\> +- Jurgis Pralgauskis \<\> diff --git a/web_tree_dynamic_colored_field/readme/CREDITS.md b/web_tree_dynamic_colored_field/readme/CREDITS.md index 705d3b30cad9..c245089844e9 100644 --- a/web_tree_dynamic_colored_field/readme/CREDITS.md +++ b/web_tree_dynamic_colored_field/readme/CREDITS.md @@ -1,3 +1,4 @@ The development of this module has been financially supported by: - Camptocamp +- Versada diff --git a/web_tree_dynamic_colored_field/readme/ROADMAP.md b/web_tree_dynamic_colored_field/readme/ROADMAP.md index d66580c7e506..16376e95ff72 100644 --- a/web_tree_dynamic_colored_field/readme/ROADMAP.md +++ b/web_tree_dynamic_colored_field/readme/ROADMAP.md @@ -4,3 +4,4 @@ `colors` attribute is no longer in the RelaxNG schema of the tree view, so we can't use it anymore. This feature has then been dropped, but could be reimplement in another way. +- Since version 17.0 coloring is written into ``style`` attribute of (td) element diff --git a/web_tree_dynamic_colored_field/readme/USAGE.md b/web_tree_dynamic_colored_field/readme/USAGE.md index d50d9b7352f2..b5950598ff01 100644 --- a/web_tree_dynamic_colored_field/readme/USAGE.md +++ b/web_tree_dynamic_colored_field/readme/USAGE.md @@ -12,7 +12,7 @@ ... - With this example, column which renders 'name' field will have its background colored in red. + With this example, column which renders 'name' field will have its **background** colored in red on customer records. - In the tree view declaration, put `options='{"fg_color": "white:customer == True"}'` attribute in the @@ -28,28 +28,12 @@ ... - With this example, column which renders 'name' field will have its text colored in white on a customer records. - -- In the tree view declaration, use - `options='"color_field": "my_color"'` attribute in the `tree` tag: - - ... - - - ... - - ... - - - ... - -- You can also use `colors="bg_color_field: my_color"` to defined the - field name that will be used for the background color of the line. + With this example, column which renders 'name' field will have its **text** colored in white on customer records. - If you want to use more than one color, you can split the attributes using ';': -``` +``` options='{"fg_color": "red:red_color == True; green:green_color == True"}' ``` @@ -65,12 +49,6 @@ Example: ... - - With this example, the content of the field named `my_color` will be used to - populate the `my_color` CSS value. Use a function field to return whichever - color you want depending on the other record values. Note that this - overrides the rest of `colors` attributes, and that you need the tree - to load your field in the first place by adding it as invisible field. ``` - Can use strings too... In the tree view declaration, put diff --git a/web_tree_dynamic_colored_field/static/description/index.html b/web_tree_dynamic_colored_field/static/description/index.html index c0a0ff8edd1d..d8a6aec808f5 100644 --- a/web_tree_dynamic_colored_field/static/description/index.html +++ b/web_tree_dynamic_colored_field/static/description/index.html @@ -406,7 +406,7 @@

Usage

</field> ... -With this example, column which renders 'name' field will have its background colored in red. +With this example, column which renders 'name' field will have its **background** colored in red on customer records.
  • In the tree view declaration, put @@ -423,27 +423,9 @@

    Usage

    </field> ... -With this example, column which renders 'name' field will have its text colored in white on a customer records. +With this example, column which renders 'name' field will have its **text** colored in white on customer records.
  • -
  • In the tree view declaration, use -options='"color_field": "my_color"' attribute in the tree -tag:

    -
    -...
    -<field name="arch" type="xml">
    -    <tree string="View name" colors="color_field: my_color" >
    -        ...
    -        <field name="my_color" invisible="1"/>
    -        ...
    -    </tree>
    -</field>
    -...
    -
    -
  • -
  • You can also use colors="bg_color_field: my_color" to defined the -field name that will be used for the background color of the line.

    -
  • If you want to use more than one color, you can split the attributes using ‘;’:

  • @@ -461,13 +443,7 @@

    Usage

    ... </tree> </field> - ... - - With this example, the content of the field named `my_color` will be used to - populate the `my_color` CSS value. Use a function field to return whichever - color you want depending on the other record values. Note that this - overrides the rest of `colors` attributes, and that you need the tree - to load your field in the first place by adding it as invisible field. + ...
    • Can use strings too… In the tree view declaration, put @@ -499,6 +475,8 @@

      Known issues / Roadmap

      colors attribute is no longer in the RelaxNG schema of the tree view, so we can’t use it anymore. This feature has then been dropped, but could be reimplement in another way.
    • +
    • Since version 17.0 coloring is written into style attribute of +(td) element
    @@ -529,6 +507,7 @@

    Contributors

  • Guewen Baconnier <guewen.baconnier@camptocamp.com>
  • Phuc Tran Thanh <phuc@trobz.com>
  • Sylvain LE GAL <https://twitter.com/legalsylvain>
  • +
  • Jurgis Pralgauskis <jurgis@versada.eu>
  • @@ -536,6 +515,7 @@

    Other credits

    The development of this module has been financially supported by:

    • Camptocamp
    • +
    • Versada
    diff --git a/web_tree_dynamic_colored_field/static/src/js/list_renderer.esm.js b/web_tree_dynamic_colored_field/static/src/js/list_renderer.esm.js new file mode 100644 index 000000000000..7b14f273a30b --- /dev/null +++ b/web_tree_dynamic_colored_field/static/src/js/list_renderer.esm.js @@ -0,0 +1,78 @@ +/** @odoo-module **/ + +import {patch} from "@web/core/utils/patch"; +import {ListRenderer} from "@web/views/list/list_renderer"; +import {evaluateBooleanExpr} from "@web/core/py_js/py"; + +patch(ListRenderer.prototype, { + /** + * @param {Object} column represents field + * @param {Record} record + * @returns {String} style code for the html element + */ + getDynamicColoredStyle(column, record) { + let style = ""; + + let color = this.getDynamicColor(column, record, "bg_color"); + if (color !== undefined) { + style += `background-color: ${color};`; + } + + color = this.getDynamicColor(column, record, "fg_color"); + if (color !== undefined) { + // $td.css('color', color); + style += `color: ${color};`; + } + + return style; + }, + + /** + * Return the `color` that has truthfull expresssion + * + * @param column {Object} represents field + * @param record {Record} + * @param color_target {String} 'bg_color' or 'fg_color' + * @returns {String | undefined} color + */ + getDynamicColor(column, record, color_target) { + if (color_target in column.options) { + const definition = column.options[color_target]; + let result = ""; + for (const color_def of definition.split(";")) { + const color_to_expression = this.pairColorParse(color_def); + if (color_to_expression !== undefined) { + const [color, expression] = color_to_expression; + if ( + evaluateBooleanExpr( + expression, + record.evalContextWithVirtualIds + ) + ) { + // We don't return first match, + // as it can be default color (with "True" expression), + // and later more precise condition may be found. + result = color; + } + } + } + return result | undefined; + } + }, + + /** + * @param {String} pairColor `color: expression` pair + * @returns {Array} undefined or array of color, expression + */ + pairColorParse: function (pairColor) { + if (pairColor !== "") { + var pairList = pairColor.split(":"), + color = pairList[0], + // If one passes a bare color instead of an expression, + // then we consider that color is to be shown in any case + expression = pairList[1] ? pairList[1] : "True"; + return [color, expression]; + } + return undefined; + }, +}); diff --git a/web_tree_dynamic_colored_field/static/src/js/web_tree_dynamic_colored_field.js b/web_tree_dynamic_colored_field/static/src/js/web_tree_dynamic_colored_field.js deleted file mode 100644 index 8998e3b6b34e..000000000000 --- a/web_tree_dynamic_colored_field/static/src/js/web_tree_dynamic_colored_field.js +++ /dev/null @@ -1,165 +0,0 @@ -odoo.define("web_tree_dynamic_colored_field", function (require) { - "use strict"; - - var ListRenderer = require("web.ListRenderer"); - var pyUtils = require("web.py_utils"); - var py = window.py; - - ListRenderer.include({ - /** - * Look up for a `color_field` or ``bg_color_field`` parameter in tree `colors` attribute - * - * @override - */ - _renderBody: function () { - if (this.arch.attrs.colors) { - var colorAttr = this.arch.attrs.colors.split(";"); - if (colorAttr.length > 0) { - var colorType = colorAttr[0].split(":")[0].trim(); - var colorField = colorAttr[0].split(":")[1].trim(); - // Validate the presence of that field in tree view - if ( - this.state.data.length && - colorField in this.state.data[0].data - ) { - if (colorType === "color_field") { - this.colorField = colorField; - } else if (colorType === "bg_color_field") { - this.bgColorField = colorField; - } - } else { - console.warn( - "No field named '" + colorField + "' present in view." - ); - } - } - } - return this._super(); - }, - /** - * Colorize a cell during it's render - * - * @override - */ - _renderBodyCell: function (record, node) { - var $td = this._super.apply(this, arguments); - var ctx = this.getEvalContext(record); - this.applyColorize($td, record, node, ctx); - return $td; - }, - - /** - * Colorize the current cell depending on expressions provided. - * - * @param {Element} $td a tag inside a table representing a list view - * @param {Object} record - * @param {Object} node an XML node (must be a ) - * @param {Object} ctx evaluation context for the record - */ - applyColorize: function ($td, record, node, ctx) { - if (!node.attrs.options) { - return; - } - if (node.tag !== "field") { - return; - } - var treeBgColor = record.data[this.bgColorField]; - if (treeBgColor) { - $td.css("background-color", treeBgColor); - } - // Apply 's own `options` - if (!node.attrs.options) { - return; - } - if (node.tag !== "field") { - return; - } - var nodeOptions = node.attrs.options; - if (!_.isObject(nodeOptions)) { - nodeOptions = pyUtils.py_eval(nodeOptions); - } - this.applyColorizeHelper($td, nodeOptions, node, "fg_color", "color", ctx); - this.applyColorizeHelper( - $td, - nodeOptions, - node, - "bg_color", - "background-color", - ctx - ); - }, - /** - * @param {Element} $td a tag inside a table representing a list view - * @param {Object} nodeOptions a mapping of nodeOptions parameters to the color itself - * @param {Object} node an XML node (must be a ) - * @param {String} nodeAttribute an attribute of a node to apply a style onto - * @param {String} cssAttribute a real CSS-compatible attribute - * @param {Object} ctx evaluation context for the record - */ - applyColorizeHelper: function ( - $td, - nodeOptions, - node, - nodeAttribute, - cssAttribute, - ctx - ) { - if (nodeOptions[nodeAttribute]) { - var colors = _(nodeOptions[nodeAttribute].split(";")) - .chain() - .map(this.pairColors) - .value() - .filter(function CheckUndefined(value) { - return value !== undefined; - }); - for (var i = 0, len = colors.length; i < len; ++i) { - var pair = colors[i], - color = pair[0], - expression = pair[1]; - if (py.evaluate(expression, ctx).toJSON()) { - $td.css(cssAttribute, color); - } - } - } - }, - - /** - * Parse `: ` forms to - * evaluable expressions - * - * @param {String} pairColor `color: expression` pair - * @returns {Array} undefined or array of color, parsed expression, - * original expression - */ - pairColors: function (pairColor) { - if (pairColor !== "") { - var pairList = pairColor.split(":"), - color = pairList[0], - // If one passes a bare color instead of an expression, - // then we consider that color is to be shown in any case - expression = pairList[1] ? pairList[1] : "True"; - return [color, py.parse(py.tokenize(expression)), expression]; - } - return undefined; - }, - /** - * Construct domain evaluation context, mostly by passing - * record's fields's values to local scope. - * - * @param {Object} record a record to build a context from - * @returns {Object} evaluation context for the record - */ - getEvalContext: function (record) { - var ctx = _.extend({}, record.data, pyUtils.context()); - for (var key in ctx) { - var value = ctx[key]; - if (ctx[key] instanceof moment) { - // Date/datetime fields are represented w/ Moment objects - // docs: https://momentjs.com/ - ctx[key] = value.format("YYYY-MM-DD hh:mm:ss"); - } - } - return ctx; - }, - }); -}); diff --git a/web_tree_dynamic_colored_field/static/src/xml/list.xml b/web_tree_dynamic_colored_field/static/src/xml/list.xml new file mode 100644 index 000000000000..42515c439597 --- /dev/null +++ b/web_tree_dynamic_colored_field/static/src/xml/list.xml @@ -0,0 +1,19 @@ + + + + + + {{getDynamicColoredStyle(column, record)}} + + + +