diff options
Diffstat (limited to 'addons/web_editor/static/src')
5 files changed, 180 insertions, 40 deletions
diff --git a/addons/web_editor/static/src/js/common/utils.js b/addons/web_editor/static/src/js/common/utils.js index 912b4717c69..e7e2a4fad97 100644 --- a/addons/web_editor/static/src/js/common/utils.js +++ b/addons/web_editor/static/src/js/common/utils.js @@ -139,6 +139,33 @@ function _areCssValuesEqual(value1, value2, cssProp, $target) { const numValue2 = _convertValueToUnit(value2, data[1], cssProp, $target); return (Math.abs(numValue1 - numValue2) < Number.EPSILON); } +/** + * @param {string|number} name + * @returns {boolean} + */ +function _isColorCombinationName(name) { + const number = parseInt(name); + return (!isNaN(number) && number % 100 !== 0); +} +/** + * @param {string[]} colorNames + * @param {string} [prefix='bg-'] + * @returns {string[]} + */ +function _computeColorClasses(colorNames, prefix = 'bg-') { + let hasCCClasses = false; + const classes = colorNames.map(c => { + if (_isColorCombinationName(c)) { + hasCCClasses = true; + return `o_cc${c}`; + } + return (prefix + c); + }); + if (hasCCClasses) { + classes.push('o_cc'); + } + return classes; +} return { CSS_SHORTHANDS: CSS_SHORTHANDS, @@ -148,5 +175,7 @@ return { convertNumericToUnit: _convertNumericToUnit, getNumericAndUnit: _getNumericAndUnit, areCssValuesEqual: _areCssValuesEqual, + isColorCombinationName: _isColorCombinationName, + computeColorClasses: _computeColorClasses, }; }); 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 0b30bf856ce..212ab507755 100644 --- a/addons/web_editor/static/src/js/editor/snippets.options.js +++ b/addons/web_editor/static/src/js/editor/snippets.options.js @@ -1086,11 +1086,15 @@ const ColorpickerUserValueWidget = SelectUserValueWidget.extend({ * @returns {Promise} */ _renderColorPalette: function () { - const options = {}; - options.selectedColor = this._value; + const options = { + selectedColor: this._value, + }; if (this.options.dataAttributes.excluded) { options.excluded = this.options.dataAttributes.excluded.replace(/ /g, '').split(','); } + if (this.options.dataAttributes.withCombinations) { + options.withCombinations = !!this.options.dataAttributes.withCombinations; + } const oldColorPalette = this.colorPalette; this.colorPalette = new ColorPaletteWidget(this, options); if (oldColorPalette) { @@ -1108,14 +1112,17 @@ const ColorpickerUserValueWidget = SelectUserValueWidget.extend({ _updateUI: async function (color) { await this._super(...arguments); - this.colorPreviewEl.classList.remove(...this.colorPalette.getColorNames().map(c => 'bg-' + c)); + const classes = weUtils.computeColorClasses(this.colorPalette.getColorNames()); + this.colorPreviewEl.classList.remove(...classes); this.colorPreviewEl.style.removeProperty('background-color'); if (this._value) { if (ColorpickerWidget.isCSSColor(this._value)) { this.colorPreviewEl.style.backgroundColor = this._value; + } else if (weUtils.isColorCombinationName(this._value)) { + this.colorPreviewEl.classList.add('o_cc', `o_cc${this._value}`); } else { - this.colorPreviewEl.classList.add('bg-' + this._value); + this.colorPreviewEl.classList.add(`bg-${this._value}`); } } @@ -1631,9 +1638,15 @@ const SnippetOptionWidget = Widget.extend({ // other potential color names (to remove) and if we know about a prefix // (otherwise we suppose that we should use the actual related color). if (params.colorNames && params.colorPrefix) { - const classes = params.colorNames.map(c => params.colorPrefix + c); + const classes = weUtils.computeColorClasses(params.colorNames, params.colorPrefix); this.$target[0].classList.remove(...classes); + if (weUtils.isColorCombinationName(widgetValue)) { + // Those are the special color combinations classes. Just have + // to add it (and adding the potential extra class) then leave. + this.$target[0].classList.add('o_cc', `o_cc${widgetValue}`, params.extraClass); + return; + } if (params.colorNames.includes(widgetValue)) { const originalCSSValue = window.getComputedStyle(this.$target[0])[cssProps[0]]; const className = params.colorPrefix + widgetValue; @@ -1924,7 +1937,8 @@ const SnippetOptionWidget = Widget.extend({ case 'selectStyle': { if (params.colorPrefix && params.colorNames) { for (const c of params.colorNames) { - if (this.$target[0].classList.contains(params.colorPrefix + c)) { + const className = weUtils.computeColorClasses([c], params.colorPrefix)[0]; + if (this.$target[0].classList.contains(className)) { return c; } } 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 60fdec4108d..4f6d677a1cb 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 @@ -7,6 +7,7 @@ const session = require('web.session'); const {ColorpickerWidget} = require('web.Colorpicker'); const Widget = require('web.Widget'); const summernoteCustomColors = require('web_editor.rte.summernote_custom_colors'); +const weUtils = require('web_editor.utils'); const qweb = core.qweb; @@ -14,9 +15,10 @@ const ColorPaletteWidget = Widget.extend({ // ! for xmlDependencies, see loadDependencies function template: 'web_editor.snippet.option.colorpicker', events: { - 'click button': '_onColorButtonClick', - 'mouseenter button': '_onColorButtonEnter', - 'mouseleave button': '_onColorButtonLeave', + 'click .o_we_color_btn': '_onColorButtonClick', + 'mouseenter .o_we_color_btn': '_onColorButtonEnter', + 'mouseleave .o_we_color_btn': '_onColorButtonLeave', + 'click .o_we_colorpicker_switch_pane_btn': '_onSwitchPaneButtonClick', }, custom_events: { 'colorpicker_select': '_onColorPickerSelect', @@ -42,9 +44,13 @@ const ColorPaletteWidget = Widget.extend({ excluded: [], excludeSectionOf: null, $editable: $(), + withCombinations: false, }, options || {}); this.selectedColor = ''; + this.resetButton = this.options.resetButton; + this.withCombinations = this.options.withCombinations; + this.trigger_up('request_editable', {callback: val => this.options.$editable = val}); }, /** @@ -60,28 +66,20 @@ const ColorPaletteWidget = Widget.extend({ start: async function () { const res = this._super.apply(this, arguments); - const $colorSection = this.$('.o_colorpicker_sections'); - const $wrapper = $colorSection.find('.o_colorpicker_section_tabs'); + const $colorSection = this.$('.o_colorpicker_sections[data-color-tab="theme-colors"]'); const $clpicker = qweb.has_template('web_editor.colorpicker') ? $(qweb.render('web_editor.colorpicker')) : $(`<colorpicker><div class="o_colorpicker_section" data-name="common"></div></colorpicker>`); - $clpicker.appendTo($wrapper); + $clpicker.find('button').addClass('o_we_color_btn'); + $clpicker.appendTo($colorSection); // 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) { - $colorSection.find('[data-name="' + exc + '"]').addClass('d-none'); + _.each(this.options.excluded, exc => { + this.$('[data-name="' + exc + '"]').addClass('d-none'); }); if (this.options.excludeSectionOf) { - $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('<div class="o_colorpicker_section" data-name="reset"/>'); - } - this._createColorButton('', ['o_colorpicker_reset', 'float-right']).appendTo($section); + this.$('[data-name]:has([data-color="' + this.options.excludeSectionOf + '"])').addClass('d-none'); } this.el.querySelectorAll('.o_colorpicker_section').forEach(elem => { @@ -109,9 +107,14 @@ const ColorPaletteWidget = Widget.extend({ this.el.querySelectorAll('button[data-color]').forEach(elem => { const colorName = elem.dataset.color; const $color = $(elem); - $color.addClass('bg-' + colorName); + const isCCName = weUtils.isColorCombinationName(colorName); + if (isCCName) { + $color.find('.o_we_cc_preview_wrapper').addClass(`o_cc o_cc${colorName}`); + } else { + $color.addClass(`bg-${colorName}`); + } this.colorNames.push(colorName); - if (!elem.classList.contains('d-none')) { + if (!isCCName && !elem.classList.contains('d-none')) { const color = ColorpickerWidget.normalizeCSSColor(this.style.getPropertyValue('--' + colorName).trim()); this.colorToColorNames[color] = colorName; } @@ -140,7 +143,7 @@ const ColorPaletteWidget = Widget.extend({ this.colorPicker = new ColorpickerWidget(this, { defaultColor: defaultColor, }); - await this.colorPicker.prependTo(this.$el); + await this.colorPicker.prependTo($colorSection); return res; }, /** @@ -217,12 +220,8 @@ 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); + const $button = this._createColorButton(color, classes); + return $button.appendTo($themeSection); }, /** * Return a color button. @@ -233,7 +232,7 @@ const ColorPaletteWidget = Widget.extend({ */ _createColorButton: function (color, classes) { return $('<button/>', { - class: classes.join(' '), + class: 'o_we_color_btn ' + classes.join(' '), style: 'background-color:' + color + ';', }); }, @@ -338,6 +337,20 @@ const ColorPaletteWidget = Widget.extend({ target: this.colorPicker.el, }, 'custom_color_picked'); }, + /** + * @private + * @param {Event} ev + */ + _onSwitchPaneButtonClick(ev) { + ev.stopPropagation(); + this.el.querySelectorAll('.o_we_colorpicker_switch_pane_btn').forEach(el => { + el.classList.remove('active'); + }); + ev.currentTarget.classList.add('active'); + this.el.querySelectorAll('.o_colorpicker_sections').forEach(el => { + el.classList.toggle('d-none', el.dataset.colorTab !== ev.currentTarget.dataset.target); + }); + }, }); //------------------------------------------------------------------------------ diff --git a/addons/web_editor/static/src/scss/wysiwyg_snippets.scss b/addons/web_editor/static/src/scss/wysiwyg_snippets.scss index 97b4659535e..1631c70f075 100644 --- a/addons/web_editor/static/src/scss/wysiwyg_snippets.scss +++ b/addons/web_editor/static/src/scss/wysiwyg_snippets.scss @@ -635,13 +635,55 @@ body.editor_enable.editor_has_snippets { } } +.o_we_cc_preview_container { + background-color: white !important; + background-image: url("/web_editor/static/src/img/transparent.png") !important; + font-family: sans-serif !important; + font-size: 16px !important; +} +.o_we_cc_preview_wrapper > * { + margin-top: 0 !important; + margin-bottom: 0 !important; +} +.o_we_color_combination_btn_text { + color: inherit !important; + font-family: inherit !important; + font-size: 0.8em !important; +} +.o_we_color_combination_btn_title { + font-size: 1em !important; +} +.o_we_color_combination_btn_btn { + padding: 0 1px !important; + border-radius: 2px !important; + font-size: 0.8em !important; +} + // SNIPPET OPTIONS .colorpicker { - padding: 3px; + .d-flex .btn.o_we_colorpicker_switch_pane_btn { + font-size: 0.8em !important; + + &.active { + border-bottom: 1px solid $o-we-handles-accent-color !important; + } + + + .o_colorpicker_reset { + border: 1px solid var(--o-we-color) !important; + border-radius: 2px !important; + font-size: 0.8em !important; + padding: 0 2px !important; + align-self: center; + } + + &:focus { + box-shadow: none; + } + } .o_colorpicker_sections { font-size: 14px; - button { + .o_we_color_btn, .o_we_color_combination_btn { float: left; width: percentage(1 / 8); padding-top: percentage(1 / 8); @@ -664,12 +706,30 @@ body.editor_enable.editor_has_snippets { } } + .o_we_color_combination_btn { + float: none; + width: 100%; + padding: 0; + margin: 0; + background-clip: padding-box; + border-top: 4px solid transparent; + border-bottom: 4px solid transparent; + + > .o_we_cc_preview_wrapper { + padding: 8px; + } + + + .o_we_color_combination_btn { + margin-top: -4px; + } + } + .o_colorpicker_section { > :first-child { margin: 4px 0; } - button { + .o_we_color_btn { position: relative; &.selected::before { @@ -688,7 +748,7 @@ body.editor_enable.editor_has_snippets { } &[data-name="transparent_grayscale"], &[data-name="theme"], &[data-name="reset"] { - button { + .o_we_color_btn { @include o-alpha-button-preview; &::before, &::after { diff --git a/addons/web_editor/static/src/xml/snippets.xml b/addons/web_editor/static/src/xml/snippets.xml index 59d3d28d5cc..aee5de39106 100644 --- a/addons/web_editor/static/src/xml/snippets.xml +++ b/addons/web_editor/static/src/xml/snippets.xml @@ -43,11 +43,35 @@ <!-- options --> <div t-name="web_editor.snippet.option.colorpicker" class="colorpicker"> - - <div class="o_colorpicker_sections"> - <div class="tab-content o_colorpicker_section_tabs"/> + <div class="d-flex px-1"> + <button t-if="widget.withCombinations" type="button" class="btn flex-grow-1 o_we_colorpicker_switch_pane_btn active" data-target="color-combinations">Combinations</button> + <button type="button" t-attf-class="btn flex-grow-1 o_we_colorpicker_switch_pane_btn #{widget.withCombinations ? '' : 'active'}" data-target="theme-colors">Custom</button> + <button t-if="widget.resetButton" type="button" class="btn flex-grow-1 o_we_color_btn o_colorpicker_reset"> + <t t-if="widget.withCombinations">None</t> + <t t-else="">Reset</t> + </button> + </div> + <div t-attf-class="o_colorpicker_sections #{widget.withCombinations ? '' : 'd-none'} px-1" data-color-tab="color-combinations"> + <!-- List all Presets --> + <t t-foreach="5" t-as="i"> + <t t-call="web_editor.colorpalette.combination.btn"> + <t t-set="number" t-value="i + 1"/> + </t> + </t> </div> + <div t-attf-class="o_colorpicker_sections #{widget.withCombinations ? 'd-none' : ''} p-1" data-color-tab="theme-colors"/> </div> + <t t-name="web_editor.colorpalette.combination.btn"> + <button type="button" class="o_we_color_btn o_we_color_combination_btn o_we_cc_preview_container" + t-att-data-color="number" t-attf-title="Preset #{number}"> + <div class="o_we_cc_preview_wrapper d-flex justify-content-between align-items-end"> + <h1 class="o_we_color_combination_btn_title">Title</h1> + <p class="o_we_color_combination_btn_text flex-grow-1">Text</p> + <span class="o_we_color_combination_btn_btn btn btn-sm btn-primary o_btn_preview mr-1"><small>Button</small></span> + <span class="o_we_color_combination_btn_btn btn btn-sm btn-secondary o_btn_preview"><small>Button</small></span> + </div> + </button> + </t> <t t-name="web_editor.many2one.button"> <div class="btn-group"> |
