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.

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