You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

131 lines
5.2 KiB

  1. odoo.define('web_tree_dynamic_colored_field', function (require) {
  2. 'use strict';
  3. var ListRenderer = require('web.ListRenderer');
  4. var pyeval = require('web.pyeval');
  5. ListRenderer.include({
  6. /**
  7. * Look up for a `color_field` parameter in tree `colors` attribute
  8. *
  9. * @override
  10. */
  11. _renderBody: function () {
  12. if (this.arch.attrs.colors) {
  13. var colorAttr = this.arch.attrs.colors.split(';')
  14. .filter(color >= color.trim().startsWith('color_field'));
  15. if (colorAttr.length > 0) {
  16. var colorField = colorAttr[0].split(':')[1].trim();
  17. // validate the presence of that field in tree view
  18. if (this.state.data.length && colorField in this.state.data[0].data) {
  19. this.colorField = colorField;
  20. } else {
  21. console.warn(
  22. "No field named '" + colorField + "' present in view."
  23. );
  24. }
  25. }
  26. }
  27. return this._super();
  28. },
  29. /**
  30. * Colorize a cell during it's render
  31. *
  32. * @override
  33. */
  34. _renderBodyCell: function (record, node, colIndex, options) {
  35. var $td = this._super.apply(this, arguments);
  36. var ctx = this.getEvalContext(record);
  37. this.applyColorize($td, record, node, ctx);
  38. return $td;
  39. },
  40. /**
  41. * Colorize the current cell depending on expressions provided.
  42. *
  43. * @param {Query Node} $td a <td> tag inside a table representing a list view
  44. * @param {Object} node an XML node (must be a <field>)
  45. */
  46. applyColorize: function ($td, record, node, ctx) {
  47. // safely resolve value of `color_field` given in <tree>
  48. var treeColor = record.data[this.colorField];
  49. if (treeColor) {
  50. $td.css('color', treeColor);
  51. }
  52. // apply <field>'s own `options`
  53. if (!node.attrs.options) { return; }
  54. if (node.tag !== 'field') { return; }
  55. var nodeOptions = node.attrs.options;
  56. if (!_.isObject(nodeOptions)) {
  57. nodeOptions = pyeval.py_eval(nodeOptions);
  58. }
  59. this.applyColorizeHelper($td, nodeOptions, node, 'fg_color', 'color', ctx);
  60. this.applyColorizeHelper($td, nodeOptions, node, 'bg_color', 'background-color', ctx);
  61. },
  62. /**
  63. * @param {Object} nodeOptions a mapping of nodeOptions parameters to the color itself
  64. * @param {Object} node an XML node (must be a <field>)
  65. * @param {string} nodeAttribute an attribute of a node to apply a style onto
  66. * @param {string} cssAttribute a real CSS-compatible attribute
  67. */
  68. applyColorizeHelper: function ($td, nodeOptions, node, nodeAttribute, cssAttribute, ctx) {
  69. if (nodeOptions[nodeAttribute]) {
  70. var colors = _(nodeOptions[nodeAttribute].split(';'))
  71. .chain()
  72. .map(this.pairColors)
  73. .value()
  74. .filter(function CheckUndefined(value, index, ar) {
  75. return value !== undefined;
  76. });
  77. for (var i=0, len=colors.length; i<len; ++i) {
  78. var pair = colors[i],
  79. color = pair[0],
  80. expression = pair[1];
  81. if (py.evaluate(expression, ctx).toJSON()) {
  82. $td.css(cssAttribute, color);
  83. }
  84. }
  85. }
  86. },
  87. /**
  88. * Parse `<color>: <field> <operator> <value>` forms to
  89. * evaluable expressions
  90. *
  91. * @param {string} pairColor `color: expression` pair
  92. */
  93. pairColors: function (pairColor) {
  94. if (pairColor !== "") {
  95. var pairList = pairColor.split(':'),
  96. color = pairList[0],
  97. // if one passes a bare color instead of an expression,
  98. // then we consider that color is to be shown in any case
  99. expression = pairList[1]? pairList[1] : 'True';
  100. return [color, py.parse(py.tokenize(expression)), expression];
  101. }
  102. return undefined;
  103. },
  104. /**
  105. * Construct domain evaluation context, mostly by passing
  106. * record's fields's values to local scope.
  107. *
  108. * @param {Object} record a record to build a context from
  109. */
  110. getEvalContext: function (record) {
  111. var ctx = _.extend(
  112. {},
  113. record.data,
  114. pyeval.context()
  115. );
  116. for (var key in ctx) {
  117. var value = ctx[key];
  118. if (ctx[key] instanceof moment) {
  119. // date/datetime fields are represented w/ Moment objects
  120. // docs: https://momentjs.com/
  121. ctx[key] = value.format('YYYY-MM-DD hh:mm:ss');
  122. }
  123. }
  124. return ctx;
  125. }
  126. });
  127. });