From 99910b526ed792235a778863b993d10cf4e77cd7 Mon Sep 17 00:00:00 2001 From: fja-odoo Date: Thu, 16 Apr 2020 14:00:41 +0000 Subject: [PATCH] [IMP] web, web_editor: integrate colorpicker in colorpalette Stop using the colorpicker dialog in the colorpalette widget. Integrate the colorpicker widget in the colorpalette widget. Of course, the colorpicker behaviors are adapted so that no useless rerendering of the colorpalette is made on each value change. The 'reset color' button is now a button like the other colors, no need for a 'color_reset' event anymore. It is removed and replaced with a 'color_picked' event with color being an empty string. The clear button now also work on hover. Also, the topbar colorpalette for text and text background colors now has the exact same behaviour as the background colorpalette: adding color preview on hover. The colorpalette now also closes on 'enter' keypress. Part of https://github.com/odoo/odoo/pull/46088 task-2195313 closes odoo/odoo#46088 Related: odoo/enterprise#8696 Signed-off-by: Quentin Smetz (qsm) --- .../web/static/src/js/widgets/colorpicker.js | 76 +++++++-- addons/web/static/src/scss/colorpicker.scss | 70 +++++---- addons/web/static/src/xml/colorpicker.xml | 60 +++----- .../static/src/js/editor/rte.summernote.js | 29 ++-- .../static/src/js/editor/snippets.options.js | 50 +++++- .../static/src/js/editor/summernote.js | 12 +- .../src/js/wysiwyg/widgets/color_palette.js | 144 ++++++++++-------- .../web_editor/static/src/scss/wysiwyg.scss | 2 +- .../static/src/scss/wysiwyg_snippets.scss | 25 ++- addons/web_editor/static/src/xml/snippets.xml | 10 +- addons/web_editor/views/editor.xml | 2 +- 11 files changed, 294 insertions(+), 186 deletions(-) diff --git a/addons/web/static/src/js/widgets/colorpicker.js b/addons/web/static/src/js/widgets/colorpicker.js index 87a46e77a2f..6b17431259a 100644 --- a/addons/web/static/src/js/widgets/colorpicker.js +++ b/addons/web/static/src/js/widgets/colorpicker.js @@ -12,6 +12,8 @@ var ColorpickerWidget = Widget.extend({ xmlDependencies: ['/web/static/src/xml/colorpicker.xml'], template: 'Colorpicker', events: { + 'click': '_onClick', + 'keypress': '_onKeypress', 'mousedown .o_color_pick_area': '_onMouseDownPicker', 'mousedown .o_color_slider': '_onMouseDownSlider', 'mousedown .o_opacity_slider': '_onMouseDownOpacitySlider', @@ -39,18 +41,22 @@ var ColorpickerWidget = Widget.extend({ this.sliderFlag = false; this.opacitySliderFlag = false; this.colorComponents = {}; + this.uniqueId = _.uniqueId('colorpicker'); - 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; + // Needs to be bound on document to work in all possible cases. + const $document = $(document); + $document.on(`mousemove.${this.uniqueId}`, _.throttle((ev) => { + this._onMouseMovePicker(ev); + this._onMouseMoveSlider(ev); + this._onMouseMoveOpacitySlider(ev); + }, 50)); + $document.on(`mouseup.${this.uniqueId}`, _.throttle((ev) => { + if (this.pickerFlag || this.sliderFlag || this.opacitySliderFlag) { + this._colorSelected(); + } + this.pickerFlag = false; + this.sliderFlag = false; + this.opacitySliderFlag = false; }, 10)); this.options = _.clone(options); @@ -66,12 +72,17 @@ var ColorpickerWidget = Widget.extend({ this.$opacitySlider = this.$('.o_opacity_slider'); this.$opacitySliderPointer = this.$('.o_opacity_pointer'); + const resizeObserver = new window.ResizeObserver(() => { + this._updateUI(); + }); + resizeObserver.observe(this.el); + var defaultColor = this.options.defaultColor || '#FF0000'; var rgba = ColorpickerWidget.convertCSSColorToRgba(defaultColor); if (rgba) { this._updateRgba(rgba.red, rgba.green, rgba.blue, rgba.opacity); } - this._updateUI(); + this.previewActive = true; return this._super.apply(this, arguments); }, /** @@ -79,7 +90,7 @@ var ColorpickerWidget = Widget.extend({ */ destroy: function () { this._super.apply(this, arguments); - $(document.body).off('.colorpicker'); + $(document).off(`.${this.uniqueId}`); }, //-------------------------------------------------------------------------- @@ -206,6 +217,14 @@ var ColorpickerWidget = Widget.extend({ ); this._updateCssColor(); }, + /** + * Trigger an event to annonce that the widget value has changed + * + * @private + */ + _colorSelected: function () { + this.trigger_up('colorpicker_select', this.colorComponents); + }, /** * Updates css color representation. * @@ -219,12 +238,35 @@ var ColorpickerWidget = Widget.extend({ _.extend(this.colorComponents, {cssColor: ColorpickerWidget.convertRgbaToCSSColor(r, g, b, a)} ); + if (this.previewActive) { + this.trigger_up('colorpicker_preview', this.colorComponents); + } }, //-------------------------------------------------------------------------- // Handlers //-------------------------------------------------------------------------- + /** + * @private + * @param {Event} ev + */ + _onKeypress: function (ev) { + if (ev.charCode === $.ui.keyCode.ENTER) { + if (ev.target.tagName === 'INPUT') { + this._onChangeInputs(ev); + } + ev.preventDefault(); + this.trigger_up('enter_key_color_colorpicker'); + } + }, + /** + * @param {Event} ev + */ + _onClick: function (ev) { + ev.originalEvent.__isColorpickerClick = true; + $(ev.target).find('> .o_opacity_pointer, > .o_slider_pointer, > .o_picker_pointer').addBack('.o_opacity_pointer, .o_slider_pointer, .o_picker_pointer').focus(); + }, /** * Updates color when the user starts clicking on the picker. * @@ -233,6 +275,7 @@ var ColorpickerWidget = Widget.extend({ */ _onMouseDownPicker: function (ev) { this.pickerFlag = true; + ev.preventDefault(); this._onMouseMovePicker(ev); }, /** @@ -265,6 +308,7 @@ var ColorpickerWidget = Widget.extend({ */ _onMouseDownSlider: function (ev) { this.sliderFlag = true; + ev.preventDefault(); this._onMouseMoveSlider(ev); }, /** @@ -293,6 +337,7 @@ var ColorpickerWidget = Widget.extend({ */ _onMouseDownOpacitySlider: function (ev) { this.opacitySliderFlag = true; + ev.preventDefault(); this._onMouseMoveOpacitySlider(ev); }, /** @@ -344,6 +389,7 @@ var ColorpickerWidget = Widget.extend({ break; } this._updateUI(); + this._colorSelected(); }, }); @@ -598,7 +644,9 @@ const ColorpickerDialog = Dialog.extend({ */ start: function () { const proms = [this._super(...arguments)]; - this.colorPicker = new ColorpickerWidget(this, this.options); + this.colorPicker = new ColorpickerWidget(this, _.extend({ + colorPreview: true, + }, this.options)); proms.push(this.colorPicker.appendTo(this.$el)); return Promise.all(proms); }, diff --git a/addons/web/static/src/scss/colorpicker.scss b/addons/web/static/src/scss/colorpicker.scss index ab8d2fb4a90..c53b31198d4 100644 --- a/addons/web/static/src/scss/colorpicker.scss +++ b/addons/web/static/src/scss/colorpicker.scss @@ -2,8 +2,8 @@ .o_colorpicker_widget { .o_color_pick_area { position: relative; - width: 200px; - height: 200px; + width: 79%; + height: 125px; background-image: linear-gradient(to bottom, hsl(0, 0%, 100%) 0%, hsla(0, 0%, 100%, 0) 50%, hsla(0, 0%, 0%, 0) 50%, hsl(0, 0%, 0%) 100%), linear-gradient(to right, hsl(0, 0%, 50%) 0%, hsla(0, 0%, 50%, 0) 100%); cursor: crosshair; @@ -18,14 +18,15 @@ } .o_color_slider { position: relative; - width: 18px; - height: 200px; + margin-left: 2.5%; + width: 4%; + height: 125px; background: linear-gradient(#F00 0%, #FF0 16.66%, #0F0 33.33%, #0FF 50%, #00F 66.66%, #F0F 83.33%, #F00 100%); cursor: pointer; .o_slider_pointer { - @include o-position-absolute($left: -4px); - width: 23px; + @include o-position-absolute($left: -100%); + width: 300%; height: 6px; border: 1px solid white; box-shadow: 0px 0px 2px 1px gray('600'); @@ -33,13 +34,14 @@ } .o_opacity_slider { position: relative; - width: 18px; - height: 200px; + margin-left: 5%; + width: 4%; + height: 125px; cursor: pointer; .o_opacity_pointer { - @include o-position-absolute($left: -4px); - width: 23px; + @include o-position-absolute($left: -100%); + width: 300%; height: 6px; border: 1px solid white; box-shadow: 0px 0px 2px 1px gray('600'); @@ -52,34 +54,38 @@ background-image: url('/web_editor/static/src/img/transparent.png'); } } + .o_color_picker_inputs { + padding-top: 3px; + font-size: 12px; display: flex; - flex-direction: column; - & > .form-row { - flex: 1; - .col { - display: flex; - flex-direction: column; - justify-content: space-around; - } - } + .o_color_preview_parent { + height: 20px; + width: 30px; background-image: url('/web_editor/static/src/img/transparent.png'); - } - .o_color_preview { - padding: 0.375rem 1.2rem; - } - .form-group { - margin-bottom: 0.3rem; - label { - margin-bottom: 0px; - font-size: 0.7rem; - font-weight: bold; + .o_color_preview { + height: inherit; + display: block; } } - .form-control { - height: 24px !important; - font-weight: bold; + .o_hex_div { + width: 38%; + display: flex; + border: 1px solid; + margin-right: 2%; + } + .o_rgba_div { + width: 60%; + border: 1px solid; + display: flex; + } + input { + width: 100%; + border: none; + height: 18px; + background-color: inherit; + color: inherit; } } } diff --git a/addons/web/static/src/xml/colorpicker.xml b/addons/web/static/src/xml/colorpicker.xml index 957bef83146..7fb81da8711 100644 --- a/addons/web/static/src/xml/colorpicker.xml +++ b/addons/web/static/src/xml/colorpicker.xml @@ -3,44 +3,30 @@
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+ +
+
hex
+
+ + + + + + RGBA + + RGB
-
-
-
- -
- -
-
-
- - -
-
- - -
-
- - -
-
- - -
-
-
diff --git a/addons/web_editor/static/src/js/editor/rte.summernote.js b/addons/web_editor/static/src/js/editor/rte.summernote.js index 68b00afb564..514dfdfe85c 100644 --- a/addons/web_editor/static/src/js/editor/rte.summernote.js +++ b/addons/web_editor/static/src/js/editor/rte.summernote.js @@ -30,11 +30,15 @@ var tplButton = renderer.getTemplate().button; var tplIconButton = renderer.getTemplate().iconButton; var tplDropdown = renderer.getTemplate().dropdown; -var applyColor = function (target, eventName, color) { +const processAndApplyColor = function (target, eventName, color, preview) { + if (!color) { + color = 'inherit'; + } else if (!ColorpickerWidget.isCSSColor(color)) { + color = (eventName === "foreColor" ? 'text-' : 'bg-') + color; + } var layoutInfo = dom.makeLayoutInfo(target); - $.summernote.pluginEvents[eventName](undefined, eventHandler.modules.editor, layoutInfo, color); + $.summernote.pluginEvents[eventName](undefined, eventHandler.modules.editor, layoutInfo, color, preview); }; - // Update and change the popovers content, and add history button renderer.createPalette = function ($container, options) { const $dropdownContent = $container.find(".colorPalette"); @@ -46,6 +50,10 @@ renderer.createPalette = function ($container, options) { const mutex = new concurrency.MutexedDropPrevious(); const $dropdown = $(elem).closest('.btn-group, .dropdown'); let manualOpening = false; + // Prevent dropdown closing on colorpicker click + $dropdown.on('hide.bs.dropdown', ev => { + return !(ev.clickEvent && ev.clickEvent.originalEvent && ev.clickEvent.originalEvent.__isColorpickerClick); + }); $dropdown.on('show.bs.dropdown', () => { if (manualOpening) { return true; @@ -62,14 +70,15 @@ renderer.createPalette = function ($container, options) { $editable: rte.Class.prototype.editable(), // Our parent is the root widget, we can't retrieve the editable section from it... selectedColor: $(targetElement).css(eventName === "foreColor" ? 'color' : 'backgroundColor'), }); - colorpicker.on('color_picked', null, ev => { - let color = ev.data.color; - if (!ColorpickerWidget.isCSSColor(color)) { - color = (eventName === "foreColor" ? 'text-' : 'bg-') + color; - } - applyColor(ev.data.target, eventName, color); + colorpicker.on('custom_color_picked color_picked', null, ev => { + processAndApplyColor(ev.data.target, eventName, ev.data.color); + }); + colorpicker.on('color_hover color_leave', null, ev => { + processAndApplyColor(ev.data.target, eventName, ev.data.color, true); + }); + colorpicker.on('enter_key_color_colorpicker', null, () => { + $dropdown.children('.dropdown-toggle').dropdown('hide'); }); - colorpicker.on('color_reset', null, ev => applyColor(ev.data.target, eventName, 'inherit')); return colorpicker.replace(hookEl).then(() => { if (oldColorpicker) { oldColorpicker.destroy(); diff --git a/addons/web_editor/static/src/js/editor/snippets.options.js b/addons/web_editor/static/src/js/editor/snippets.options.js index 35fd4cef7c7..50b56ad435a 100644 --- a/addons/web_editor/static/src/js/editor/snippets.options.js +++ b/addons/web_editor/static/src/js/editor/snippets.options.js @@ -726,8 +726,10 @@ const SelectUserValueWidget = UserValueWidget.extend({ _onClick: function () { if (!this.menuTogglerEl.classList.contains('active')) { this.trigger_up('user_value_widget_opening'); + this.menuTogglerEl.classList.add('active'); + } else { + this.close(); } - this.menuTogglerEl.classList.toggle('active'); const activeButton = this._userValueWidgets.find(widget => widget.isActive()); if (activeButton) { this.menuEl.scrollTop = activeButton.el.offsetTop - (this.menuEl.offsetHeight / 2); @@ -980,10 +982,11 @@ const MultiUserValueWidget = UserValueWidget.extend({ const ColorpickerUserValueWidget = SelectUserValueWidget.extend({ className: (SelectUserValueWidget.prototype.className || '') + ' o_we_so_color_palette', custom_events: _.extend({}, SelectUserValueWidget.prototype.custom_events, { + 'custom_color_picked': '_onCustomColorPicked', 'color_picked': '_onColorPicked', 'color_hover': '_onColorHovered', 'color_leave': '_onColorLeft', - 'color_reset': '_onColorReset', + 'enter_key_color_colorpicker': '_onEnterKey' }), /** @@ -1009,6 +1012,17 @@ const ColorpickerUserValueWidget = SelectUserValueWidget.extend({ // Public //-------------------------------------------------------------------------- + /** + * @override + */ + close: function () { + this._super(...arguments); + if (this._customColorValue && this._customColorValue !== this._value) { + this._value = this._customColorValue; + this._customColorValue = false; + this._onUserValueChange(); + } + }, /** * @override */ @@ -1024,6 +1038,9 @@ const ColorpickerUserValueWidget = SelectUserValueWidget.extend({ if (typeof this._previewColor === 'string') { return this._previewColor; } + if (typeof this._customColorValue === 'string') { + return this._customColorValue; + } let value = this._super(...arguments); if (value && this.options.dataAttributes.hasOwnProperty('cssCompatible') && !ColorpickerWidget.isCSSColor(value)) { @@ -1093,6 +1110,16 @@ const ColorpickerUserValueWidget = SelectUserValueWidget.extend({ // Handlers //-------------------------------------------------------------------------- + /** + * Called when a custom color is selected -> preview the color + * and set the current value. Update of this value on close + * + * @private + * @param {Event} ev + */ + _onCustomColorPicked: function (ev) { + this._customColorValue = ev.data.color; + }, /** * Called when a color button is clicked -> confirms the preview. * @@ -1101,6 +1128,7 @@ const ColorpickerUserValueWidget = SelectUserValueWidget.extend({ */ _onColorPicked: function (ev) { this._previewColor = false; + this._customColorValue = false; this._value = ev.data.color; this._onUserValueChange(ev); }, @@ -1125,14 +1153,20 @@ const ColorpickerUserValueWidget = SelectUserValueWidget.extend({ this._onUserValueReset(ev); }, /** - * Called when the color reset button is clicked -> removes all color - * classes and color styles. - * * @private */ - _onColorReset: function (ev) { - this._value = ''; - this._onUserValueChange(ev); + _onEnterKey: function () { + this.close(); + }, + /** + * @override + */ + _onClick: function (ev) { + // Do not close the colorpalette on colorpicker click + if (!ev.originalEvent.__isColorpickerClick) { + this._super(...arguments); + } + ev.stopPropagation(); }, }); diff --git a/addons/web_editor/static/src/js/editor/summernote.js b/addons/web_editor/static/src/js/editor/summernote.js index 80ccd3d0c82..1053e0699bc 100644 --- a/addons/web_editor/static/src/js/editor/summernote.js +++ b/addons/web_editor/static/src/js/editor/summernote.js @@ -2307,12 +2307,14 @@ $.summernote.pluginEvents.color = function (event, editor, layoutInfo, sObjColor if (foreColor) { $.summernote.pluginEvents.foreColor(event, editor, layoutInfo, foreColor); } if (backColor) { $.summernote.pluginEvents.backColor(event, editor, layoutInfo, backColor); } }; -$.summernote.pluginEvents.foreColor = function (event, editor, layoutInfo, foreColor) { +$.summernote.pluginEvents.foreColor = function (event, editor, layoutInfo, foreColor, preview) { var $editable = layoutInfo.editable(); $.summernote.pluginEvents.applyFont(event, editor, layoutInfo, foreColor, null, null); - editor.afterCommand($editable); + if (!preview) { + editor.afterCommand($editable); + } }; -$.summernote.pluginEvents.backColor = function (event, editor, layoutInfo, backColor) { +$.summernote.pluginEvents.backColor = function (event, editor, layoutInfo, backColor, preview) { var $editable = layoutInfo.editable(); var r = range.create(); if (!r) return; @@ -2328,7 +2330,9 @@ $.summernote.pluginEvents.backColor = function (event, editor, layoutInfo, backC return; } $.summernote.pluginEvents.applyFont(event, editor, layoutInfo, null, backColor, null); - editor.afterCommand($editable); + if (!preview) { + editor.afterCommand($editable); + } }; options.onCreateLink = function (sLinkUrl) { diff --git a/addons/web_editor/static/src/js/wysiwyg/widgets/color_palette.js b/addons/web_editor/static/src/js/wysiwyg/widgets/color_palette.js index 48d9c560c3c..75943dac1e7 100644 --- a/addons/web_editor/static/src/js/wysiwyg/widgets/color_palette.js +++ b/addons/web_editor/static/src/js/wysiwyg/widgets/color_palette.js @@ -3,7 +3,7 @@ odoo.define('web_editor.ColorPalette', function (require) { const core = require('web.core'); const session = require('web.session'); -const {ColorpickerDialog, ColorpickerWidget} = require('web.Colorpicker'); +const {ColorpickerWidget} = require('web.Colorpicker'); const Widget = require('web.Widget'); const summernoteCustomColors = require('web_editor.rte.summernote_custom_colors'); @@ -18,8 +18,10 @@ const ColorPaletteWidget = Widget.extend({ 'click button': '_onColorButtonClick', 'mouseenter button': '_onColorButtonEnter', 'mouseleave button': '_onColorButtonLeave', - 'click .o_colorpicker_reset': '_onColorResetButtonClick', - 'click .o_add_custom_color': '_onCustomColorButtonClick', + }, + custom_events: { + 'colorpicker_select': '_onColorPickerSelect', + 'colorpicker_preview': '_onColorPickerPreview', }, /** * @override @@ -71,7 +73,7 @@ const ColorPaletteWidget = Widget.extend({ /** * @override */ - start: function () { + start: async function () { const res = this._super.apply(this, arguments); const $colorSection = this.$('.o_colorpicker_sections'); @@ -81,14 +83,6 @@ const ColorPaletteWidget = Widget.extend({ : $(`
`); $clpicker.appendTo($wrapper); - this.el.querySelectorAll('.o_colorpicker_section').forEach(elem => { - $(elem).prepend('
' + (elem.dataset.display || '') + '
'); - }); - - if (this.options.resetButton) { - this.$('.o_colorpicker_reset').removeClass('d-none'); - } - // Remove excluded palettes (note: only hide them to still be able // to remove their related colors on the DOM target) _.each(this.options.excluded, function (exc) { @@ -98,6 +92,18 @@ const ColorPaletteWidget = Widget.extend({ $colorSection.find('[data-name]:has([data-color="' + this.options.excludeSectionOf + '"])').addClass('d-none'); } + if (this.options.resetButton) { + let $section = this.$('.o_colorpicker_section[data-name="theme"]'); + if ($section.hasClass('d-none')) { + $section = $section.insertBefore('
'); + } + this._createColorButton('', ['o_colorpicker_reset', 'float-right']).appendTo($section); + } + + this.el.querySelectorAll('.o_colorpicker_section').forEach(elem => { + $(elem).prepend('
' + (elem.dataset.display || '') + '
'); + }); + // Render common colors if (!this.options.excluded.includes('common')) { const $commonColorSection = this.$('[data-name="common"]'); @@ -126,20 +132,26 @@ const ColorPaletteWidget = Widget.extend({ } }); - // Render custom colors - this._buildCustomColors(); - - // Compute selected color + // Select selected Color and build customColors. + // If no color is selected selectedColor is an empty string (transparent is interpreted as no color) if (this.options.selectedColor) { const selectedColor = ColorpickerWidget.normalizeCSSColor(this.options.selectedColor); if (selectedColor !== 'rgba(0, 0, 0, 0)') { this.selectedColor = this.colorToColorNames[selectedColor] || selectedColor; } - const selectedButton = this.el.querySelector(`button[data-color="${this.selectedColor}"], button[style*="background-color:${this.selectedColor};"]`); - if (selectedButton) { - selectedButton.classList.add('selected'); - } } + this._buildCustomColors(); + this._markSelectedColor(); + + // Colorpicker + let defaultColor = this.selectedColor; + if (defaultColor && !ColorpickerWidget.isCSSColor(defaultColor)) { + defaultColor = ColorpickerWidget.normalizeCSSColor(this.style.getPropertyValue('--' + defaultColor).trim()); + } + this.colorPicker = new ColorpickerWidget(this, { + defaultColor: defaultColor, + }); + await this.colorPicker.prependTo(this.$el); // Add those at the very end so that we don't mark hidden colors as // selected. We want those colors to appear as selected custom colors if @@ -184,6 +196,7 @@ const ColorPaletteWidget = Widget.extend({ if (this.options.excluded.includes('custom')) { return; } + this.el.querySelectorAll('.o_custom_color').forEach(el => el.remove()); const existingColors = new Set(this.summernoteCustomColorsArray.concat( Object.keys(this.colorToColorNames) )); @@ -239,6 +252,11 @@ const ColorPaletteWidget = Widget.extend({ _addCustomColorButton: function (color, classes = []) { classes.push('o_custom_color'); const $themeSection = this.$('.o_colorpicker_section[data-name="theme"]'); + // There is 8 buttons on a colorpalette line, the clear button is always the last of the first line. + if (this.options.resetButton && $themeSection.find('button').length < 8) { + const $resetButton = $themeSection.find('.o_colorpicker_reset'); + return this._createColorButton(color, classes).insertBefore($resetButton); + } return this._createColorButton(color, classes).appendTo($themeSection); }, /** @@ -263,20 +281,34 @@ const ColorPaletteWidget = Widget.extend({ */ _getButtonInfo: function (buttonEl) { const bgColor = buttonEl.style.backgroundColor; - const color = bgColor ? ColorpickerWidget.normalizeCSSColor(bgColor) : buttonEl.dataset.color; return { - color: color, + color: bgColor ? ColorpickerWidget.normalizeCSSColor(bgColor) : buttonEl.dataset.color || '', target: buttonEl, }; }, /** - * Set the selectedColor and inform parents + * Set the selectedColor and trigger an event * - * @param {Object} colorInfo + * @param {Object} color + * @param {string} eventName */ - _selectColor: function (colorInfo) { + _selectColor: function (colorInfo, eventName) { this.selectedColor = colorInfo.color = this.colorToColorNames[colorInfo.color] || colorInfo.color; - this.trigger_up('color_picked', colorInfo); + this.trigger_up(eventName, colorInfo); + this._buildCustomColors(); + this._markSelectedColor(); + }, + /** + * Mark the selected color + * + * @private + */ + _markSelectedColor: function () { + this.el.querySelectorAll('button.selected').forEach(el => el.classList.remove('selected')); + const selectedButton = this.el.querySelector(`button[data-color="${this.selectedColor}"], button[style*="background-color:${this.selectedColor};"]`); + if (selectedButton) { + selectedButton.classList.add('selected'); + } }, //-------------------------------------------------------------------------- @@ -292,7 +324,7 @@ const ColorPaletteWidget = Widget.extend({ _onColorButtonClick: function (ev) { const buttonEl = ev.currentTarget; const colorInfo = this._getButtonInfo(buttonEl); - this._selectColor(colorInfo); + this._selectColor(colorInfo, 'color_picked'); }, /** * Called when a color button is entered. @@ -305,59 +337,41 @@ const ColorPaletteWidget = Widget.extend({ this.trigger_up('color_hover', this._getButtonInfo(ev.currentTarget)); }, /** - * Called when a color button is left. + * Called when a color button is left the data color is the color currently selected. * * @private * @param {Event} ev */ _onColorButtonLeave: function (ev) { ev.stopPropagation(); - const selected = this.el.querySelector('button.selected'); - let params = null; - if (selected) { - params = this._getButtonInfo(selected); - } else { - params = { - color: '', - }; - } - params.target = ev.target; - this.trigger_up('color_leave', params); - }, - /** - * Called when the color reset button is clicked. - * - * @private - * @param {Event} ev - */ - _onColorResetButtonClick: function (ev) { - this.selectedColor = ''; - this.trigger_up('color_reset', { + this.trigger_up('color_leave', { + color: this.selectedColor, target: ev.target, }); }, /** - * Called when the custom color button is clicked. + * Called when an update is made on the colorpicker. * * @private * @param {Event} ev */ - _onCustomColorButtonClick: async function (ev) { - const target = ev.target; - let selectedColor = this.selectedColor; - if (!ColorpickerWidget.isCSSColor(selectedColor)) { - selectedColor = ColorpickerWidget.normalizeCSSColor(this.style.getPropertyValue('--' + selectedColor).trim()); - } - const colorpicker = new ColorpickerDialog(this, { - defaultColor: selectedColor, + _onColorPickerPreview: function (ev) { + this.trigger_up('color_hover', { + color: ev.data.cssColor, + target: this.colorPicker.el, }); - colorpicker.on('colorpicker:saved', this, ev => { - this._selectColor({ - color: ev.data.cssColor, - target: target, - }); - }); - colorpicker.open(); + }, + /** + * Called when a color is selected on the colorpicker (mouseup). + * + * @private + * @param {Event} ev + */ + _onColorPickerSelect: function (ev) { + this._selectColor({ + color: ev.data.cssColor, + target: this.colorPicker.el, + }, 'custom_color_picked'); }, }); diff --git a/addons/web_editor/static/src/scss/wysiwyg.scss b/addons/web_editor/static/src/scss/wysiwyg.scss index cac357a4af3..fca0e5345ea 100644 --- a/addons/web_editor/static/src/scss/wysiwyg.scss +++ b/addons/web_editor/static/src/scss/wysiwyg.scss @@ -174,7 +174,7 @@ $o-we-zindex: $o-we-overlay-zindex + 1 !default; } } .note-color ul.show { - min-width: 172px !important; + min-width: 216px !important; } // ANIMATIONS diff --git a/addons/web_editor/static/src/scss/wysiwyg_snippets.scss b/addons/web_editor/static/src/scss/wysiwyg_snippets.scss index d0c6026dfa8..6ad06de9082 100644 --- a/addons/web_editor/static/src/scss/wysiwyg_snippets.scss +++ b/addons/web_editor/static/src/scss/wysiwyg_snippets.scss @@ -422,7 +422,7 @@ body.editor_enable.editor_has_snippets { z-index: $zindex-dropdown; border: 1px solid var(--o-we-color); background-color: var(--o-we-bg-color-dark); - max-height: 400px; + max-height: 600px; overflow-y: auto; } } @@ -633,6 +633,7 @@ body.editor_enable.editor_has_snippets { // SNIPPET OPTIONS .colorpicker { + padding: 3px; .o_colorpicker_sections { font-size: 14px; @@ -643,6 +644,20 @@ body.editor_enable.editor_has_snippets { margin: 0; border: none; box-shadow: inset 0 0 0 1px var(--o-we-bg-color-dark); + + &.o_colorpicker_reset { + background-color: transparent; + + &::before { + @include o-position-absolute(0, 0, 0, 0); + font-family: FontAwesome !important; + content: "\f00d" !important;// fa-times + display: flex; + justify-content: center; + align-items: center; + color: $o-we-color-danger; + } + } } .o_colorpicker_section { @@ -668,7 +683,7 @@ body.editor_enable.editor_has_snippets { } } - &[data-name="transparent_grayscale"], &[data-name="theme"] { + &[data-name="transparent_grayscale"], &[data-name="theme"], &[data-name="reset"] { button { @include o-alpha-button-preview; @@ -894,7 +909,7 @@ body.editor_enable.editor_has_snippets { > div { display: flex; align-items: center; - + > * { margin: 0 2px; border: 1px solid $o-we-color-text-normal; @@ -903,7 +918,7 @@ body.editor_enable.editor_has_snippets { opacity: 0.5; transition: opacity 400ms ease 0s; cursor: pointer; - + &:hover { opacity: 1; } @@ -915,7 +930,7 @@ body.editor_enable.editor_has_snippets { .o_move_handle { padding: 3px 4px; cursor: move; - + &::before, &::after { content: '\f141'; display: block; diff --git a/addons/web_editor/static/src/xml/snippets.xml b/addons/web_editor/static/src/xml/snippets.xml index a240431b6e8..59d3d28d5cc 100644 --- a/addons/web_editor/static/src/xml/snippets.xml +++ b/addons/web_editor/static/src/xml/snippets.xml @@ -44,15 +44,7 @@
-
- +
diff --git a/addons/web_editor/views/editor.xml b/addons/web_editor/views/editor.xml index 584725eb515..ad0e4245b76 100644 --- a/addons/web_editor/views/editor.xml +++ b/addons/web_editor/views/editor.xml @@ -231,7 +231,7 @@
-
+