web_tree_dynamic_colored_field
							parent
							
								
									17c44284c7
								
							
						
					
					
						commit
						bd0bd2bfc1
					
				|  | @ -0,0 +1,108 @@ | ||||||
|  | .. image:: https://img.shields.io/badge/license-LGPL--3-blue.svg | ||||||
|  |    :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html | ||||||
|  |    :alt: License: LGPL-3 | ||||||
|  | 
 | ||||||
|  | ============================ | ||||||
|  | Colorize field in tree views | ||||||
|  | ============================ | ||||||
|  | 
 | ||||||
|  | This module aims to add support for dynamically coloring fields in tree view | ||||||
|  | according to data in the record. | ||||||
|  | 
 | ||||||
|  | It provides attributes on fields with the similar syntax as the ``colors`` attribute | ||||||
|  | in tree tags. | ||||||
|  | 
 | ||||||
|  | Further, it provides a ``color_field`` attribute on tree tags's ``colors`` to use | ||||||
|  | a field's value as color. | ||||||
|  | 
 | ||||||
|  | Features | ||||||
|  | ======== | ||||||
|  | 
 | ||||||
|  | * Add attribute ``bg_color`` on field's ``options`` to color background of a cell in tree view | ||||||
|  | * Add attribute ``fg_color`` on field's ``options`` to change text color of a cell in tree view | ||||||
|  | * Add attribute ``color_field`` on the tree element's ``colors`` to use as color | ||||||
|  | 
 | ||||||
|  | Usage | ||||||
|  | ===== | ||||||
|  | 
 | ||||||
|  | * In the tree view declaration, put ``options='{"bg_color": "red: customer==True"}`` attribute in the ``field`` tag:: | ||||||
|  | 
 | ||||||
|  |     ... | ||||||
|  |     <field name="arch" type="xml"> | ||||||
|  |         <tree string="View name"> | ||||||
|  |             ... | ||||||
|  |             <field name="name" options='{"bg_color": "red: customer == True"}'/> | ||||||
|  |             ... | ||||||
|  |         </tree> | ||||||
|  |     </field> | ||||||
|  |     ... | ||||||
|  |      | ||||||
|  |     With this example, column which renders 'name' field will have its background colored in red. | ||||||
|  | 
 | ||||||
|  | * In the tree view declaration, put ``options='{"fg_color": "white:customer == True"}'`` attribute in the ``field`` tag:: | ||||||
|  | 
 | ||||||
|  |     ... | ||||||
|  |     <field name="arch" type="xml"> | ||||||
|  |         <tree string="View name"> | ||||||
|  |             ... | ||||||
|  |             <field name="name" options='{"fg_color": "white:customer == True"}'/> | ||||||
|  |             ... | ||||||
|  |         </tree> | ||||||
|  |     </field> | ||||||
|  |     ... | ||||||
|  |      | ||||||
|  |     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:: | ||||||
|  | 
 | ||||||
|  |     ... | ||||||
|  |     <field name="arch" type="xml"> | ||||||
|  |         <tree string="View name" colors="color_field: my_color" > | ||||||
|  |             ... | ||||||
|  |             <field name="my_color" invisible="1"/> | ||||||
|  |             ... | ||||||
|  |         </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. | ||||||
|  | 
 | ||||||
|  | **Note that you should always use single quotes for fields' ``options`` and wrap nested values in double quotes since ``options`` is a JSON object.** | ||||||
|  | 
 | ||||||
|  | Bug Tracker | ||||||
|  | =========== | ||||||
|  | 
 | ||||||
|  | Bugs are tracked on `GitHub Issues | ||||||
|  | <https://github.com/OCA/web/issues>`_. In case of trouble, please | ||||||
|  | check there if your issue has already been reported. If you spotted it first, | ||||||
|  | help us smash it by providing a detailed and welcomed feedback. | ||||||
|  | 
 | ||||||
|  | Credits | ||||||
|  | ======= | ||||||
|  | 
 | ||||||
|  | Contributors | ||||||
|  | ------------ | ||||||
|  | 
 | ||||||
|  | * Damien Crier <damien.crier@camptocamp.com> | ||||||
|  | * Holger Brunn <hbrunn@therp.nl> | ||||||
|  | * Artem Kostyuk <a.kostyuk@mobilunity.com> | ||||||
|  | * Guewen Baconnier <guewen.baconnier@camptocamp.com> | ||||||
|  | 
 | ||||||
|  | Maintainer | ||||||
|  | ---------- | ||||||
|  | 
 | ||||||
|  | .. image:: https://odoo-community.org/logo.png | ||||||
|  |    :alt: Odoo Community Association | ||||||
|  |    :target: https://odoo-community.org | ||||||
|  | 
 | ||||||
|  | This module is maintained by the OCA. | ||||||
|  | 
 | ||||||
|  | OCA, or the Odoo Community Association, is a nonprofit organization whose | ||||||
|  | mission is to support the collaborative development of Odoo features and | ||||||
|  | promote its widespread use. | ||||||
|  | 
 | ||||||
|  | To contribute to this module, please visit https://odoo-community.org. | ||||||
|  | @ -0,0 +1,2 @@ | ||||||
|  | # Copyright 2015-2018 Camptocamp SA, Damien Crier | ||||||
|  | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). | ||||||
|  | @ -0,0 +1,19 @@ | ||||||
|  | # Copyright 2015-2018 Camptocamp SA, Damien Crier | ||||||
|  | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). | ||||||
|  | { | ||||||
|  |     'name': 'Colorize field in tree views', | ||||||
|  |     'summary': 'Allows you to dynamically color fields on tree views', | ||||||
|  |     'category': 'Hidden/Dependency', | ||||||
|  |     'version': '11.0.1.0.1', | ||||||
|  |     'depends': ['web'], | ||||||
|  |     'author': "Camptocamp, Therp BV, Odoo Community Association (OCA)", | ||||||
|  |     'license': 'AGPL-3', | ||||||
|  |     'website': 'https://github.com/OCA/web', | ||||||
|  |     'demo': [ | ||||||
|  |         "demo/res_users.xml", | ||||||
|  |     ], | ||||||
|  |     'data': [ | ||||||
|  |         'views/web_tree_dynamic_colored_field.xml', | ||||||
|  |     ], | ||||||
|  |     'installable': True, | ||||||
|  | } | ||||||
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 9.2 KiB | 
|  | @ -0,0 +1,134 @@ | ||||||
|  | odoo.define('web_tree_dynamic_colored_field', function (require) { | ||||||
|  |     'use strict'; | ||||||
|  | 
 | ||||||
|  |     var ListRenderer = require('web.ListRenderer'); | ||||||
|  |     var pyeval = require('web.pyeval'); | ||||||
|  | 
 | ||||||
|  |     ListRenderer.include({ | ||||||
|  |         /** | ||||||
|  |          * Look up for a `color_field` parameter in tree `colors` attribute | ||||||
|  |          * | ||||||
|  |          * @override | ||||||
|  |          */ | ||||||
|  |         _renderBody: function () { | ||||||
|  |             if (this.arch.attrs.colors) { | ||||||
|  |                 var colorAttr = this.arch.attrs.colors.split(';') | ||||||
|  |                     .filter(color => color.trim().startsWith('color_field')); | ||||||
|  |                 if (colorAttr.length > 0) { | ||||||
|  |                     var colorField = colorAttr[0].split(':')[1].trim(); | ||||||
|  |                     // validate the presence of that field in tree view
 | ||||||
|  |                     var fieldNames = _(this.columns).map( | ||||||
|  |                         (value) => { return value.attrs.name; } | ||||||
|  |                     ); | ||||||
|  |                     if (fieldNames.indexOf(colorField) === -1) { | ||||||
|  |                         console.warn( | ||||||
|  |                             "No field named '" + colorField + "' present in view." | ||||||
|  |                         ); | ||||||
|  |                     } else { | ||||||
|  |                         this.colorField = colorField; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             return this._super(); | ||||||
|  |         }, | ||||||
|  |         /** | ||||||
|  |          * Colorize a cell during it's render | ||||||
|  |          * | ||||||
|  |          * @override | ||||||
|  |          */ | ||||||
|  |         _renderBodyCell: function (record, node, colIndex, options) { | ||||||
|  |             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 {Query Node} $td a <td> tag inside a table representing a list view | ||||||
|  |          * @param {Object} node an XML node (must be a <field>) | ||||||
|  |          */ | ||||||
|  |         applyColorize: function ($td, record, node, ctx) { | ||||||
|  |             // safely resolve value of `color_field` given in <tree>
 | ||||||
|  |             var treeColor = record.data[this.colorField]; | ||||||
|  |             if (treeColor) { | ||||||
|  |                 $td.css('color', treeColor); | ||||||
|  |             } | ||||||
|  |             // apply <field>'s own `options`
 | ||||||
|  |             if (!node.attrs.options) { return; } | ||||||
|  |             if (node.tag !== 'field') { return; } | ||||||
|  |             var nodeOptions = node.attrs.options; | ||||||
|  |             if (!_.isObject(nodeOptions)) { | ||||||
|  |                 nodeOptions = pyeval.py_eval(nodeOptions); | ||||||
|  |             } | ||||||
|  |             this.applyColorizeHelper($td, nodeOptions, node, 'fg_color', 'color', ctx); | ||||||
|  |             this.applyColorizeHelper($td, nodeOptions, node, 'bg_color', 'background-color', ctx); | ||||||
|  |         }, | ||||||
|  |         /** | ||||||
|  |          * @param {Object} nodeOptions a mapping of nodeOptions parameters to the color itself | ||||||
|  |          * @param {Object} node an XML node (must be a <field>) | ||||||
|  |          * @param {string} nodeAttribute an attribute of a node to apply a style onto | ||||||
|  |          * @param {string} cssAttribute a real CSS-compatible attribute | ||||||
|  |          */ | ||||||
|  |         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, index, ar) { | ||||||
|  |                         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 `<color>: <field> <operator> <value>` forms to | ||||||
|  |          * evaluatable expressions | ||||||
|  |          * | ||||||
|  |          * @param {string} pairColor `color: expression` pair | ||||||
|  |          */ | ||||||
|  |         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 | ||||||
|  |          */ | ||||||
|  |         getEvalContext: function (record) { | ||||||
|  |             var ctx = _.extend( | ||||||
|  |                 {}, | ||||||
|  |                 record.data, | ||||||
|  |                 pyeval.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; | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  | }); | ||||||
|  | @ -0,0 +1,8 @@ | ||||||
|  | <?xml version="1.0" encoding="utf-8"?> | ||||||
|  | <odoo> | ||||||
|  |     <template id="assets_backend" name="web_tree_dynamic_colored_field assets" inherit_id="web.assets_backend"> | ||||||
|  |         <xpath expr="." position="inside"> | ||||||
|  |             <script type="text/javascript" src="/web_tree_dynamic_colored_field/static/src/js/web_tree_dynamic_colored_field.js"></script> | ||||||
|  |         </xpath> | ||||||
|  |     </template> | ||||||
|  | </odoo> | ||||||
		Loading…
	
		Reference in New Issue