diff options
| author | Robot Odoo <xmo+roboto@odoo.com> | 2019-01-17 19:17:22 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-01-17 19:17:22 +0100 |
| commit | 22efdfbecc1e8c688000be98903a5a355d1ece8a (patch) | |
| tree | e097625171b2b67ed5f5288ced67446608b9f633 /addons/web/static/src | |
| parent | f50fbc6d168f0d17fa1e932b4ecc968a92b02b65 (diff) | |
| parent | eb0ea8efbe932c140302e9f8da33a839bcd73146 (diff) | |
[IMP] web_editor,*: Refactoring the wysiwyg editor and 'html' field
_Task #1878502_
- The refactoring allows for the update of the _Summernote_ library.
- A new structure was created by transforming all the plugins in the library using the odoo inheritance system. Plugins are easier to implement with the `AbstractPlugin` to add Odoo behaviors.
- The cleanup allowed us to remove some of the code that was in `web_editor` but only used in `website` or in `mass_mailing`. Some parts are still in `web_editor` but will be moved at a later time.
- The refactoring also had to be done in order to create a set of unit tests. Each behavior can be tested, including the behaviors performed as a consequence of keyboard interactions (for instance: Enter, Tab...).
- From now on, the methods of the library (in this case _Summernote_) can no longer be called by other modules or files. Only the `wysiwyg` widgets can access it, to simplify the updating process. The `wysiwyg` object serves as an interface. A lot of tests are done, ensuring the consistency of outside behavior.
- Depending on the options the snippets will be loaded or not, the editor will be in an iframe or not... all of this is transparent from the outside.
- Regarding iframes, all controllers related to editing have been removed: the new API no longer needs them. This speeds up loading, eases testing and removes complexity for the same features.
**PUBLIC FEATURES**
There are several public methods on the `Wysiwyg` class:
- `Wysiwyg.prepare (WidgetParent)` which returns a deferred resolved when the library (xml, lazy, assets...) is loaded.
- `Wysiwyg.getRange (DOM)` returns the range (selection in the dom)
- `Wysiwyg.setRange (startNode, startOffset, endNode, endOffset)` which creates a range (selection in the dom)
- `Wysiwyg.setRangeFromNode (DOM, options)` that creates a range from an element (option available to select all, start or end)
A jQuery selector was added: `:o_editable`, which indicates whether the current element is editable. That is, if it is contained in a tag with the attribute `contentEditable = "true"` or in a tag with the class `o_editable`.
Several methods are also present:
- `focusIn`: makes a focus and places the cursor at the beginning of the element
- `focusInEnd`: makes a focus and places the cursor at the end of the element
- `selectContent`: makes a focus and selects the content
**HTML FIELD**
The HTML field can receive different options:
- `style-inline:` {boolean} transforms a class into an inline style when saving and vice versa when reading.
- `no-attachment`: {boolean} prevents the use of attachments (in media dialog)
- `cssEdit`: {xml_id} to use a template containing the css to loaded in an iframe when editing
- `cssReadonly`: {xml_id} to use a template containing the css to load into an iframe when viewing in readonly
- `snippets`: {xml_id} snippets template (can be used with or without `cssEdit`)
- `wrapper`: {template} qweb static template (containing a tag: `id = "wrapper"`) that will include the content during editing (removed on save)
**MASS MAILING**
A widget was created for `mass mailing`. There are now two fields: `body_html` and `body_arch`.
`body_arch` contains the code with the class without conversion into inline style, useful when editing and one with the inline style that is visible in readonly mode and sent by email.
Advantage: no spreading errors, able to update css/theme, able to do more changes when converting to inline style so that a maximum of mail clients have an impeccable rendering.
**REMAINING KNOW ISSUES**
These will be fixed in a subsequent commit.
- A few committed changes made during the development of this refactoring were lost in the process and should be reapplied.
- Some minor (mostly range) issues remain with Firefox.
--
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
closes odoo/odoo#29775
Diffstat (limited to 'addons/web/static/src')
| -rw-r--r-- | addons/web/static/src/js/core/ajax.js | 58 | ||||
| -rw-r--r-- | addons/web/static/src/js/core/domain.js | 5 | ||||
| -rw-r--r-- | addons/web/static/src/js/report/client_action.js | 63 | ||||
| -rw-r--r-- | addons/web/static/src/js/report/report.editor.js | 81 | ||||
| -rw-r--r-- | addons/web/static/src/js/report/report.js | 44 | ||||
| -rw-r--r-- | addons/web/static/src/js/widgets/colorpicker.js | 497 | ||||
| -rw-r--r-- | addons/web/static/src/scss/fields.scss | 2 | ||||
| -rw-r--r-- | addons/web/static/src/scss/report.editor.scss | 26 | ||||
| -rw-r--r-- | addons/web/static/src/scss/report_backend.scss | 2 | ||||
| -rw-r--r-- | addons/web/static/src/xml/colorpicker.xml | 62 | ||||
| -rw-r--r-- | addons/web/static/src/xml/report.xml | 11 |
11 files changed, 89 insertions, 762 deletions
diff --git a/addons/web/static/src/js/core/ajax.js b/addons/web/static/src/js/core/ajax.js index f844cbdaed4..1821d5a3be4 100644 --- a/addons/web/static/src/js/core/ajax.js +++ b/addons/web/static/src/js/core/ajax.js @@ -1,6 +1,7 @@ odoo.define('web.ajax', function (require) { "use strict"; +var config = require('web.config'); var core = require('web.core'); var utils = require('web.utils'); var time = require('web.time'); @@ -503,6 +504,55 @@ var loadXML = (function () { }; })(); +/** + * Loads a template file according to the given xmlId. + * + * @param {string} [xmlId] - the template xmlId + * @returns {Deferred} resolved with an object + * cssLibs: list of css files + * cssContents: list of style tag contents + * jsLibs: list of JS files + * jsContents: list of script tag contents + */ +var loadAsset = (function () { + var cache = {}; + + var load = function loadAsset(xmlId) { + if (cache[xmlId]) { + return $.when(cache[xmlId]); + } + var params = { + args: [xmlId, { + debug: config.debug + }], + kwargs: { + context: odoo.session_info.user_context, + }, + method: 'render_template', + model: 'ir.ui.view', + }; + return rpc('/web/dataset/call_kw/ir.ui.view/render_template', params).then(function (xml) { + var $xml = $(xml); + cache[xmlId] = { + cssLibs: $xml.filter('link[href]:not([type="image/x-icon"])').map(function () { + return $(this).attr('href'); + }).get(), + cssContents: $xml.filter('style').map(function () { + return $(this).html(); + }).get(), + jsLibs: $xml.filter('script[src]').map(function () { + return $(this).attr('src'); + }).get(), + jsContents: $xml.filter('script:not([src])').map(function () { + return $(this).html(); + }).get(), + }; + return cache[xmlId]; + }); + }; + + return load; +})(); /** * Loads the given js and css libraries. Note that the ajax loadJS and loadCSS methods @@ -515,6 +565,8 @@ var loadXML = (function () { * parallel. * @param {Array<string>} [libs.cssLibs=[]] A list of css files, to be loaded in * parallel + * @param {Array<string>} [libs.assetLibs=[]] A list of xmlId. The loaded template + * contains the script and link to be loaded * * @returns {Deferred} */ @@ -534,6 +586,11 @@ function loadLibs (libs) { _.each(libs.cssLibs || [], function (url) { defs.push(ajax.loadCSS(url)); }); + _.each(libs.assetLibs || [], function (xmlId) { + defs.push(loadAsset(xmlId).then(function (asset) { + return loadLibs(asset); + })); + }); return $.when.apply($, defs); } @@ -544,6 +601,7 @@ var ajax = { loadCSS: loadCSS, loadJS: loadJS, loadXML: loadXML, + loadAsset: loadAsset, loadLibs: loadLibs, get_file: get_file, post: post, diff --git a/addons/web/static/src/js/core/domain.js b/addons/web/static/src/js/core/domain.js index c31feb61a8f..97642f8e438 100644 --- a/addons/web/static/src/js/core/domain.js +++ b/addons/web/static/src/js/core/domain.js @@ -104,8 +104,13 @@ var Domain = collections.Tree.extend({ ); case "like": return (fieldValue.toLowerCase().indexOf(this._data[2].toLowerCase()) >= 0); + case "=like": + var regExp = new RegExp(this._data[2].toLowerCase().replace(/([.\[\]\{\}\+\*])/g, '\\\$1').replace(/%/g, '.*')); + return regExp.test(fieldValue.toLowerCase()); case "ilike": return (fieldValue.indexOf(this._data[2]) >= 0); + case "=ilike": + return new RegExp(this._data[2].replace(/%/g, '.*'), 'i').test(fieldValue); default: throw new Error(_.str.sprintf( "Domain %s uses an unsupported operator", diff --git a/addons/web/static/src/js/report/client_action.js b/addons/web/static/src/js/report/client_action.js index 3a520e3d6b5..099ee77a527 100644 --- a/addons/web/static/src/js/report/client_action.js +++ b/addons/web/static/src/js/report/client_action.js @@ -2,7 +2,6 @@ odoo.define('report.client_action', function (require) { 'use strict'; var AbstractAction = require('web.AbstractAction'); -var config = require('web.config'); var core = require('web.core'); var session = require('web.session'); var utils = require('report.utils'); @@ -11,8 +10,6 @@ var QWeb = core.qweb; var AUTHORIZED_MESSAGES = [ - 'report.editor:save_ok', - 'report.editor:discard_ok', 'report:do_action', ]; @@ -28,8 +25,6 @@ var ReportAction = AbstractAction.extend({ this.action_manager = parent; this._title = options.display_name || options.name; - this.edit_mode_available = false; - this.in_edit_mode = false; this.report_url = options.report_url; // Extra info that will be useful to build a qweb-pdf action. @@ -49,20 +44,12 @@ var ReportAction = AbstractAction.extend({ self.trusted_origin = utils.build_origin(trusted_protocol, trusted_host); self.$buttons = $(QWeb.render('report.client_action.ControlButtons', {})); - self.$buttons.on('click', '.o_report_edit', self.on_click_edit); self.$buttons.on('click', '.o_report_print', self.on_click_print); - self.$buttons.on('click', '.o_report_save', self.on_click_save); - self.$buttons.on('click', '.o_report_discard', self.on_click_discard); self._update_control_panel(); // Load the report in the iframe. Note that we use a relative URL. self.iframe.src = self.report_url; - - // Once the iframe is loaded, check if we can edit the report. - self.iframe.onload = function () { - self._on_iframe_loaded(); - }; }); }, @@ -82,31 +69,12 @@ var ReportAction = AbstractAction.extend({ $(window).off('message', this.on_message_received); }, - _on_iframe_loaded: function () { - var editable = $(this.iframe).contents().find('html').data('editable'); - if (editable === 1) { - this.edit_mode_available = true; - this._update_control_panel(); - } - }, - _update_control_panel: function () { this.updateControlPanel({ cp_content: { $buttons: this.$buttons, }, }); - this._update_control_panel_buttons(); - }, - - /** - * Helper allowing to toggle groups of buttons in the control panel - * according to the `this.in_edit_mode` flag. - */ - _update_control_panel_buttons: function () { - this.$buttons.filter('div.o_report_edit_mode').toggle(this.in_edit_mode); - this.$buttons.filter('div.o_report_no_edit_mode').toggle(! this.in_edit_mode); - this.$buttons.filter('div.o_edit_mode_available').toggle(config.debug && this.edit_mode_available && ! this.in_edit_mode); }, /** @@ -128,18 +96,6 @@ var ReportAction = AbstractAction.extend({ } switch(message) { - case 'report.editor:save_ok': - // Reload the iframe in order to disable the editor. - this.iframe.src = this.report_url; - this.in_edit_mode = false; - this._update_control_panel_buttons(); - break; - case 'report.editor:discard_ok': - // Reload the iframe in order to disable the editor. - this.iframe.src = this.report_url; - this.in_edit_mode = false; - this._update_control_panel_buttons(); - break; case 'report:do_action': return this.do_action(ev.originalEvent.data.action); default: @@ -157,25 +113,6 @@ var ReportAction = AbstractAction.extend({ this.iframe.contentWindow.postMessage(message, this.trusted_origin); }, - on_click_edit: function () { - // We reload the iframe with a special query string to enable the editor. - if (this.report_url.indexOf('?') === -1) { - this.iframe.src = this.report_url + '?enable_editor=1'; - } else { - this.iframe.src = this.report_url + '&enable_editor=1'; - } - this.in_edit_mode = true; - this._update_control_panel_buttons(); - }, - - on_click_discard: function () { - this._post_message('report.editor:ask_discard'); - }, - - on_click_save: function () { - this._post_message('report.editor:ask_save'); - }, - on_click_print: function () { var action = { 'type': 'ir.actions.report', diff --git a/addons/web/static/src/js/report/report.editor.js b/addons/web/static/src/js/report/report.editor.js deleted file mode 100644 index 4955417a759..00000000000 --- a/addons/web/static/src/js/report/report.editor.js +++ /dev/null @@ -1,81 +0,0 @@ -odoo.define('report.editor', function (require) { -'use strict'; - -require('web.dom_ready') -var core = require('web.core'); -var utils = require('report.utils'); -var editor = require('web_editor.editor'); -var options = require('web_editor.snippets.options'); - -var web_base_url = $('html').attr('web-base-url'); -var trusted_host = utils.get_host_from_url(web_base_url); -var trusted_protocol = utils.get_protocol_from_url(web_base_url); -var trusted_origin = utils.build_origin(trusted_protocol, trusted_host); - -// Patch the editor's behavior when it is launched inside an iframe. -if (window.self !== window.top) { - - // And now we chain some deferred to `save` and `cancel` in order to inform - // the report's client action that the actions are done. - editor.Class.include({ - save: function () { - // Force to not reload - return this._super(false).then(function () { - window.parent.postMessage('report.editor:save_ok', trusted_origin); - }); - }, - cancel: function () { - // Force to not reload - return this._super(false).then(function () { - window.parent.postMessage('report.editor:discard_ok', trusted_origin); - }); - }, - }); - - // postMessage logic. - var AUTHORIZED_MESSAGES = [ - 'report.editor:ask_discard', - 'report.editor:ask_save', - ]; - - window.addEventListener('message', function (ev) { - // Check the origin of the received message. - var message_origin = utils.build_origin(ev.source.location.protocol, ev.source.location.host); - if (message_origin === trusted_origin) { - // Check the syntax of the received message. - var message = ev.data; - if (! _.isString(message) || (_.isString(message) && ! _.contains(AUTHORIZED_MESSAGES, message))) { - return; - } - - switch (message) { - case 'report.editor:ask_save': - core.bus.trigger('editor_save_request'); - break; - case 'report.editor:ask_discard': - core.bus.trigger('editor_discard_request'); - break; - default: - } - } - }, false); -} - -options.registry.many2one.include({ - _selectRecord: function ($li) { - this._super.apply(this, arguments); - if (this.$target.data('oe-field') !== 'partner_id') { - return; - } - - var $img = $('.header .row img:first'); - var css = window.getComputedStyle($img[0]); - $img.css('max-height', css.height+'px'); - $img.attr('src', '/web/image/res.partner/' + this.ID + '/image'); - _.defer(function () { - $img.removeClass('o_dirty'); - }); - } -}); - -}); diff --git a/addons/web/static/src/js/report/report.js b/addons/web/static/src/js/report/report.js index 50d78a9d151..9072966b19e 100644 --- a/addons/web/static/src/js/report/report.js +++ b/addons/web/static/src/js/report/report.js @@ -15,26 +15,26 @@ var trusted_host = utils.get_host_from_url(web_base_url); var trusted_protocol = utils.get_protocol_from_url(web_base_url); var trusted_origin = utils.build_origin(trusted_protocol, trusted_host); -// Allow to send commands to the webclient when the editor is disabled. -if (window.location.search.indexOf('enable_editor') === -1) { - // `do_action` command - $('[res-id][res-model][view-type]') - .wrap('<a/>') - .attr('href', '#') - .on('click', function (ev) { - ev.preventDefault(); - var action = { - 'type': 'ir.actions.act_window', - 'view_type': $(this).attr('view-type'), - 'view_mode': $(this).attr('view-mode') || $(this).attr('view-type'), - 'res_id': Number($(this).attr('res-id')), - 'res_model': $(this).attr('res-model'), - 'views': [[$(this).attr('view-id') || false, $(this).attr('view-type')]], - }; - window.parent.postMessage({ - 'message': 'report:do_action', - 'action': action, - }, trusted_origin); - }); -} +// Allow sending commands to the webclient +// `do_action` command +$('[res-id][res-model][view-type]') + .wrap('<a/>') + .attr('href', '#') + .on('click', function (ev) { + ev.preventDefault(); + var action = { + 'type': 'ir.actions.act_window', + 'view_type': $(this).attr('view-type'), + 'view_mode': $(this).attr('view-mode') || $(this).attr('view-type'), + 'res_id': Number($(this).attr('res-id')), + 'res_model': $(this).attr('res-model'), + 'views': [ + [$(this).attr('view-id') || false, $(this).attr('view-type')], + ], + }; + window.parent.postMessage({ + 'message': 'report:do_action', + 'action': action, + }, trusted_origin); + }); }); diff --git a/addons/web/static/src/js/widgets/colorpicker.js b/addons/web/static/src/js/widgets/colorpicker.js deleted file mode 100644 index e8ed888e1af..00000000000 --- a/addons/web/static/src/js/widgets/colorpicker.js +++ /dev/null @@ -1,497 +0,0 @@ -odoo.define('web.colorpicker', function (require) { -'use strict'; - -var core = require('web.core'); -var utils = require('web.utils'); -var Dialog = require('web.Dialog'); - -var _t = core._t; - -var Colorpicker = Dialog.extend({ - xmlDependencies: (Dialog.prototype.xmlDependencies || []) - .concat(['/web/static/src/xml/colorpicker.xml']), - - template: 'web.colorpicker', - events: _.extend({}, Dialog.prototype.events || {}, { - 'mousedown .o_color_pick_area': '_onMouseDownPicker', - 'mousedown .o_color_slider': '_onMouseDownSlider', - 'mousedown .o_opacity_slider': '_onMouseDownOpacitySlider', - 'change .o_color_picker_inputs' : '_onChangeInputs', - }), - - /** - * @constructor - * @param {Widget} parent - * @param {Object} [options] - * @param {string} [options.defaultColor='#FF0000'] - */ - init: function (parent, options) { - options = options || {}; - - this._super(parent, _.extend({ - size: 'medium', - title: _t('Pick a color'), - buttons: [ - {text: _t('Choose'), classes: 'btn-primary', close: true, click: this._onFinalPick.bind(this)}, - {text: _t('Discard'), close: true}, - ], - }, options)); - - this.pickerFlag = false; - this.sliderFlag = false; - this.opacitySliderFlag = false; - this.colorComponents = {}; - - var self = this; - var $body = $(document.body); - $body.on('mousemove.colorpicker', _.throttle(function (ev) { - self._onMouseMovePicker(ev); - self._onMouseMoveSlider(ev); - self._onMouseMoveOpacitySlider(ev); - }, 10)); - $body.on('mouseup.colorpicker', _.throttle(function (ev) { - self.pickerFlag = false; - self.sliderFlag = false; - self.opacitySliderFlag = false; - }, 10)); - - this.options = _.clone(options); - }, - /** - * @override - */ - start: function () { - this.$colorpickerArea = this.$('.o_color_pick_area'); - this.$colorpickerPointer = this.$('.o_picker_pointer'); - this.$colorSlider = this.$('.o_color_slider'); - this.$colorSliderPointer = this.$('.o_slider_pointer'); - this.$opacitySlider = this.$('.o_opacity_slider'); - this.$opacitySliderPointer = this.$('.o_opacity_pointer'); - - var defaultColor = this.options.defaultColor || '#FF0000'; - var rgba = defaultColor.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+(?:\.\d+)?))?\)$/); - if (rgba) { - if (rgba[4] === undefined) { - rgba[4] = 1; - } - this._updateRgba(parseInt(rgba[1]), parseInt(rgba[2]), parseInt(rgba[3]), Math.round(parseFloat(rgba[4] * 100))); - } else { - this._updateHex(defaultColor); - this._updateOpacity(100); - } - this.opened().then(this._updateUI.bind(this)); - - return this._super.apply(this, arguments); - }, - /** - * @override - */ - destroy: function () { - this._super.apply(this, arguments); - $(document.body).off('.colorpicker'); - }, - - //-------------------------------------------------------------------------- - // Private - //-------------------------------------------------------------------------- - - /** - * Updates input values, color preview, picker and slider pointer positions. - * - * @private - */ - _updateUI: function () { - var self = this; - - // Update inputs - _.each(this.colorComponents, function (value, color) { - self.$(_.str.sprintf('.o_%s_input', color)).val(value); - }); - - // Update preview - this.$('.o_color_preview').css('background-color', this.colorComponents.cssColor); - - // Update picker area and picker pointer position - this.$colorpickerArea.css('background-color', _.str.sprintf('hsl(%s, 100%%, 50%%)', this.colorComponents.hue)); - var top = (100 - this.colorComponents.lightness) * this.$colorpickerArea.height() / 100; - var left = this.colorComponents.saturation * this.$colorpickerArea.width() / 100; - this.$colorpickerPointer.css({ - top: (top - 5) + 'px', - left: (left - 5) + 'px', - }); - - // Update color slider position - var height = this.$colorSlider.height(); - var y = this.colorComponents.hue * height / 360; - this.$colorSliderPointer.css('top', Math.round(y - 2)); - - // Update opacity slider position - var heightOpacity = this.$opacitySlider.height(); - var z = heightOpacity * (1 - this.colorComponents.opacity / 100.0); - this.$opacitySliderPointer.css('top', Math.round(z - 2)); - - // Add gradient color on opacity slider - this.$opacitySlider.css('background', 'linear-gradient(' + this.colorComponents.hex + ' 0%, transparent 100%)'); - }, - /** - * Updates colors according to given hex value. Opacity is left unchanged. - * - * @private - * @param {string} hex - hexadecimal code - */ - _updateHex: function (hex) { - var rgb = Colorpicker.prototype.convertHexToRgb(hex); - if (!rgb) { - return; - } - _.extend(this.colorComponents, - {hex: hex}, - rgb, - Colorpicker.prototype.convertRgbToHsl(rgb.red, rgb.green, rgb.blue) - ); - this._updateCssColor(); - }, - /** - * Updates colors according to given RGB values. - * - * @private - * @param {integer} r - * @param {integer} g - * @param {integer} b - * @param {integer} [a] - */ - _updateRgba: function (r, g, b, a) { - var hex = Colorpicker.prototype.convertRgbToHex(r, g, b); - if (!hex) { - return; - } - _.extend(this.colorComponents, - {red: r, green: g, blue: b}, - a === undefined ? {} : {opacity: a}, - hex, - Colorpicker.prototype.convertRgbToHsl(r, g, b) - ); - this._updateCssColor(); - }, - /** - * Updates colors according to given HSL values. - * - * @private - * @param {integer} h - * @param {integer} s - * @param {integer} l - */ - _updateHsl: function (h, s, l) { - var rgb = Colorpicker.prototype.convertHslToRgb(h, s, l); - if (!rgb) { - return; - } - _.extend(this.colorComponents, - {hue: h, saturation: s, lightness: l}, - rgb, - Colorpicker.prototype.convertRgbToHex(rgb.red, rgb.green, rgb.blue) - ); - this._updateCssColor(); - }, - /** - * Updates color opacity. - * - * @private - * @param {integer} a - */ - _updateOpacity: function (a) { - if (a < 0 || a > 100) { - return; - } - _.extend(this.colorComponents, - {opacity: a} - ); - this._updateCssColor(); - }, - /** - * Updates css color representation. - * - * @private - */ - _updateCssColor: function () { - var cssColor = this.colorComponents.hex; - if (this.colorComponents.opacity !== 100) { - cssColor = _.str.sprintf('rgba(%s, %s, %s, %s)', - this.colorComponents.red, - this.colorComponents.green, - this.colorComponents.blue, - this.colorComponents.opacity / 100 - ); - } - _.extend(this.colorComponents, - {cssColor: cssColor} - ); - }, - - //-------------------------------------------------------------------------- - // Handlers - //-------------------------------------------------------------------------- - - /** - * Updates color when the user starts clicking on the picker. - * - * @private - * @param {Event} ev - */ - _onMouseDownPicker: function (ev) { - this.pickerFlag = true; - this._onMouseMovePicker(ev); - }, - /** - * Updates saturation and lightness values on mouse drag over picker. - * - * @private - * @param {Event} ev - */ - _onMouseMovePicker: function (ev) { - if (!this.pickerFlag) { - return; - } - - var offset = this.$colorpickerArea.offset(); - var top = ev.pageY - offset.top; - var left = ev.pageX - offset.left; - var saturation = Math.round(100 * left / this.$colorpickerArea.width()); - var lightness = Math.round(100 * (this.$colorpickerArea.height() - top) / this.$colorpickerArea.height()); - saturation = utils.confine(saturation, 0, 100); - lightness = utils.confine(lightness, 0, 100); - - this._updateHsl(this.colorComponents.hue, saturation, lightness); - this._updateUI(); - }, - /** - * Updates color when user starts clicking on slider. - * - * @private - * @param {Event} ev - */ - _onMouseDownSlider: function (ev) { - this.sliderFlag = true; - this._onMouseMoveSlider(ev); - }, - /** - * Updates hue value on mouse drag over slider. - * - * @private - * @param {Event} ev - */ - _onMouseMoveSlider: function (ev) { - if (!this.sliderFlag) { - return; - } - - var y = ev.pageY - this.$colorSlider.offset().top; - var hue = Math.round(360 * y / this.$colorSlider.height()); - hue = utils.confine(hue, 0, 360); - - this._updateHsl(hue, this.colorComponents.saturation, this.colorComponents.lightness); - this._updateUI(); - }, - /** - * Updates opacity when user starts clicking on opacity slider. - * - * @private - * @param {Event} ev - */ - _onMouseDownOpacitySlider: function (ev) { - this.opacitySliderFlag = true; - this._onMouseMoveOpacitySlider(ev); - }, - /** - * Updates opacity value on mouse drag over opacity slider. - * - * @private - * @param {Event} ev - */ - _onMouseMoveOpacitySlider: function (ev) { - if (!this.opacitySliderFlag) { - return; - } - - var y = ev.pageY - this.$opacitySlider.offset().top; - var opacity = Math.round(100 * (1 - y / this.$opacitySlider.height())); - opacity = utils.confine(opacity, 0, 100); - - this._updateOpacity(opacity); - this._updateUI(); - }, - /** - * Called when input value is changed -> Updates UI: Set picker and slider - * position and set colors. - * - * @private - * @param {Event} ev - */ - _onChangeInputs: function (ev) { - switch ($(ev.target).data('colorMethod')) { - case 'hex': - this._updateHex(this.$('.o_hex_input').val()); - break; - case 'rgb': - this._updateRgba( - parseInt(this.$('.o_red_input').val()), - parseInt(this.$('.o_green_input').val()), - parseInt(this.$('.o_blue_input').val()) - ); - break; - case 'hsl': - this._updateHsl( - parseInt(this.$('.o_hue_input').val()), - parseInt(this.$('.o_saturation_input').val()), - parseInt(this.$('.o_lightness_input').val()) - ); - break; - case 'opacity': - this._updateOpacity(parseInt(this.$('.o_opacity_input').val())); - break; - } - this._updateUI(); - }, - /** - * @private - */ - _onFinalPick: function () { - this.trigger_up('colorpicker:saved', this.colorComponents); - }, - - //-------------------------------------------------------------------------- - // Static - //-------------------------------------------------------------------------- - - /** - * Converts Hexadecimal code to RGB. - * - * @static - * @param {string} hex - hexadecimal code - * @returns {Object|false} contains red, green and blue - */ - convertHexToRgb: function (hex) { - if (!/^#[0-9A-F]{6}$/i.test(hex)) { - return false; - } - - return { - red: parseInt(hex.substr(1, 2), 16), - green: parseInt(hex.substr(3, 2), 16), - blue: parseInt(hex.substr(5, 2), 16), - }; - }, - /** - * Converts RGB color to HSL. - * - * @static - * @param {integer} r - * @param {integer} g - * @param {integer} b - * @returns {Object|false} contains hue, saturation and lightness - */ - convertRgbToHsl: function (r, g, b) { - if (typeof(r) !== 'number' || isNaN(r) || r < 0 || r > 255 || - typeof(g) !== 'number' || isNaN(g) || g < 0 || g > 255 || - typeof(b) !== 'number' || isNaN(b) || b < 0 || b > 255) { - return false; - } - - var red = r / 255; - var green = g / 255; - var blue = b / 255; - var maxColor = Math.max(red, green, blue); - var minColor = Math.min(red, green, blue); - var delta = maxColor - minColor; - var hue = 0; - var saturation = 0; - var lightness = (maxColor + minColor) / 2; - if (delta) { - if (maxColor === red) { - hue = (green - blue) / delta; - } - if (maxColor === green) { - hue = 2 + (blue - red) / delta; - } - if (maxColor === blue) { - hue = 4 + (red - green) / delta; - } - if (maxColor) { - saturation = delta / (1 - Math.abs(2 * lightness - 1)); - } - } - hue = 60 * hue | 0; - return { - hue: hue < 0 ? hue += 360 : hue, - saturation: (saturation * 100) | 0, - lightness: (lightness * 100) | 0, - }; - }, - /** - * Converts HSL color to RGB. - * - * @static - * @param {integer} h - * @param {integer} s - * @param {integer} l - * @returns {Object|false} contains red, green and blue - */ - convertHslToRgb: function (h, s, l) { - if (typeof(h) !== 'number' || isNaN(h) || h < 0 || h > 360 || - typeof(s) !== 'number' || isNaN(s) || s < 0 || s > 100 || - typeof(l) !== 'number' || isNaN(l) || l < 0 || l > 100) { - return false; - } - var huePrime = h / 60; - var saturation = s / 100; - var lightness = l / 100; - var chroma = saturation * (1 - Math.abs(2 * lightness - 1)); - var secondComponent = chroma * (1 - Math.abs(huePrime % 2 - 1)); - var lightnessAdjustment = lightness - chroma/2; - var precision = 255; - chroma = (chroma + lightnessAdjustment) * precision | 0; - secondComponent = (secondComponent + lightnessAdjustment) * precision | 0; - lightnessAdjustment = lightnessAdjustment * precision | 0; - if (huePrime >= 0 && huePrime < 1) { - return {red: chroma, green: secondComponent, blue: lightnessAdjustment}; - } - if (huePrime >= 1 && huePrime < 2) { - return {red: secondComponent, green: chroma, blue: lightnessAdjustment}; - } - if (huePrime >= 2 && huePrime < 3) { - return {red: lightnessAdjustment, green: chroma, blue: secondComponent}; - } - if (huePrime >= 3 && huePrime < 4) { - return {red: lightnessAdjustment, green: secondComponent, blue: chroma}; - } - if (huePrime >= 4 && huePrime < 5) { - return {red: secondComponent, green: lightnessAdjustment, blue: chroma}; - } - if (huePrime >= 5 && huePrime <= 6) { - return {red: chroma, green: lightnessAdjustment, blue: secondComponent}; - } - }, - /** - * Converts RGB color to Hexadecimal code. - * - * @static - * @param {integer} r - * @param {integer} g - * @param {integer} b - * @returns {Object|false} contains hexadecimal code - */ - convertRgbToHex: function (r, g, b) { - if (typeof(r) !== 'number' || isNaN(r) || r < 0 || r > 255 || - typeof(g) !== 'number' || isNaN(g) || g < 0 || g > 255 || - typeof(b) !== 'number' || isNaN(b) || b < 0 || b > 255) { - return false; - } - var red = r < 16 ? '0' + r.toString(16) : r.toString(16); - var green = g < 16 ? '0' + g.toString(16) : g.toString(16); - var blue = b < 16 ? '0' + b.toString(16) : b.toString(16); - return {hex: _.str.sprintf('#%s%s%s', red, green, blue)}; - }, -}); - -return Colorpicker; -}); diff --git a/addons/web/static/src/scss/fields.scss b/addons/web/static/src/scss/fields.scss index 81a59743788..27d0d13770e 100644 --- a/addons/web/static/src/scss/fields.scss +++ b/addons/web/static/src/scss/fields.scss @@ -33,7 +33,7 @@ } // Block fields - &.note-editor, &.oe_form_field_html, &.oe_form_field_html_text, &.oe_form_field_text, &.o_field_domain, &.o_graph_linechart, &.o_graph_barchart { + &.note-editor, &.oe_form_field_html, &.oe_form_field_text, &.o_field_domain, &.o_graph_linechart, &.o_graph_barchart { display: block; } diff --git a/addons/web/static/src/scss/report.editor.scss b/addons/web/static/src/scss/report.editor.scss deleted file mode 100644 index 544787ff6b2..00000000000 --- a/addons/web/static/src/scss/report.editor.scss +++ /dev/null @@ -1,26 +0,0 @@ -.o_editable { - &:not(:empty), &[data-oe-type] { - &[date-oe-model="ir.ui.view"] { - &, th, td { - &:hover { - -moz-box-shadow: #DDCC33 0 0 5px 2px inset; - -webkit-box-shadow: #DDCC33 0 0 5px 2px inset; - box-shadow: #DDCC33 0 0 5px 2px inset; - } - } - } - } -} - -body.o_in_iframe { - margin-top: 40px!important; - div#web_editor-top-edit { - > form { - display: none; - } - } - - .header, .footer { - display: none; - } -} diff --git a/addons/web/static/src/scss/report_backend.scss b/addons/web/static/src/scss/report_backend.scss index 62252c2d9f7..18ea620ce67 100644 --- a/addons/web/static/src/scss/report_backend.scss +++ b/addons/web/static/src/scss/report_backend.scss @@ -9,6 +9,6 @@ border: none; } -.o_report_no_edit_mode, .o_edit_mode_available, .o_report_edit_mode { +.o_report_buttons { display: inline-block; } diff --git a/addons/web/static/src/xml/colorpicker.xml b/addons/web/static/src/xml/colorpicker.xml deleted file mode 100644 index 4d85f3172a1..00000000000 --- a/addons/web/static/src/xml/colorpicker.xml +++ /dev/null @@ -1,62 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<templates> - <t t-name="web.colorpicker"> - <div class="o_colorpicker_widget"> - <div class="row"> - <div class="col-auto"> - <div class="o_color_pick_area d-inline-block border"> - <div class="o_picker_pointer rounded-circle"/> - </div> - <div class="o_color_slider d-inline-block ml-2 border"> - <div class="o_slider_pointer"/> - </div> - <div class="o_opacity_slider d-inline-block ml-2 border"> - <div class="o_opacity_pointer"/> - </div> - </div> - <form class="col o_color_picker_inputs"> - <div class="input-group"> - <div class="input-group-prepend o_color_preview_parent"> - <span class="input-group-text rounded-0 o_color_preview"/> - </div> - <input type="text" class="form-control o_hex_input" data-color-method="hex"/> - </div> - <div class="form-row mt-2"> - <div class="col"> - <div class="form-group"> - <label for="red">Red</label> - <input type="text" class="form-control o_red_input" data-color-method="rgb" id="red"/> - </div> - <div class="form-group"> - <label for="green">Green</label> - <input type="text" class="form-control o_green_input" data-color-method="rgb" id="green"/> - </div> - <div class="form-group"> - <label for="blue">Blue</label> - <input type="text" class="form-control o_blue_input" data-color-method="rgb" id="blue"/> - </div> - </div> - <div class="col"> - <div class="form-group"> - <label for="hue">Hue</label> - <input type="text" class="form-control o_hue_input" data-color-method="hsl" id="hue"/> - </div> - <div class="form-group"> - <label for="saturation">Saturation %</label> - <input type="text" class="form-control o_saturation_input" data-color-method="hsl" id="saturation"/> - </div> - <div class="form-group"> - <label for="lightness">Lightness %</label> - <input type="text" class="form-control o_lightness_input" data-color-method="hsl" id="lightness"/> - </div> - </div> - </div> - <div class="form-group"> - <label for="opacity">Opacity %</label> - <input type="text" class="form-control o_opacity_input" data-color-method="opacity" id="opacity"/> - </div> - </form> - </div> - </div> - </t> -</templates> diff --git a/addons/web/static/src/xml/report.xml b/addons/web/static/src/xml/report.xml index 49313e8de5a..7f08d009d53 100644 --- a/addons/web/static/src/xml/report.xml +++ b/addons/web/static/src/xml/report.xml @@ -7,15 +7,8 @@ <!-- Buttons of the Control Panel --> <t t-name="report.client_action.ControlButtons"> - <div class="o_report_no_edit_mode"> + <div class="o_report_buttons"> <button type="button" class="btn btn-primary o_report_print" title="Print">Print</button> </div> - <div class="o_edit_mode_available"> - <button type="button" class="btn btn-secondary o_report_edit" title="Edit">Edit</button> - </div> - <div class="o_report_edit_mode"> - <button type="button" class="btn btn-primary o_report_save" title="Save">Save</button> - <button type="button" class="btn btn-secondary o_report_discard" title="Discard">Discard</button> - </div> </t> -</template>
\ No newline at end of file +</template> |
