[IMP] website, web_editor, *: allow to use pre-configured gradients
*: web After this commit pre-configured gradients can be used as snippet backgrounds, snippet filters or as text effects / highlight effects. The background colors (and now gradients) are now possible to add *alongside* a color combination class (editing one does not remove the other). Background colors and gradients are mutually exclusive. Part of https://github.com/odoo/odoo/pull/73611 task-2599770 closes odoo/odoo#73611 Signed-off-by: Quentin Smetz (qsm) <qsm@odoo.com> Co-authored-by: Benoit Socias <bso@odoo.com>
This commit is contained in:
co-authored by
Benoit Socias
parent
38754b2485
commit
a48a30f954
@@ -449,4 +449,3 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import {
|
|||||||
getListMode,
|
getListMode,
|
||||||
getOuid,
|
getOuid,
|
||||||
insertText,
|
insertText,
|
||||||
|
isColorGradient,
|
||||||
nodeSize,
|
nodeSize,
|
||||||
preserveCursor,
|
preserveCursor,
|
||||||
setCursor,
|
setCursor,
|
||||||
@@ -999,27 +1000,73 @@ export class OdooEditor extends EventTarget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays the text colors (foreground ink and background highlight)
|
||||||
|
* based on the current text cursor position. For gradients, displays
|
||||||
|
* the average color of the gradient.
|
||||||
|
*
|
||||||
|
* @param {object} [params]
|
||||||
|
* @param {string} [params.foreColor] - forces the 'foreColor' in the
|
||||||
|
* toolbar instead of determining it from the cursor position
|
||||||
|
* @param {string} [params.hiliteColor] - forces the 'hiliteColor' in the
|
||||||
|
* toolbar instead of determining it from the cursor position
|
||||||
|
*/
|
||||||
updateColorpickerLabels(params = {}) {
|
updateColorpickerLabels(params = {}) {
|
||||||
const foreColor = params.foreColor || rgbToHex(document.queryCommandValue('foreColor'));
|
function hexFromColor(color) {
|
||||||
|
if (isColorGradient(color)) {
|
||||||
|
// For gradients, compute the average color
|
||||||
|
color = color.match(/gradient(.*)/)[0];
|
||||||
|
let r = 0, g = 0, b = 0, count = 0;
|
||||||
|
for (const entry of color.matchAll(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+(?:\.\d+)?))?\)/g)) {
|
||||||
|
count++;
|
||||||
|
r += parseInt(entry[1], 10);
|
||||||
|
g += parseInt(entry[2], 10);
|
||||||
|
b += parseInt(entry[3], 10);
|
||||||
|
}
|
||||||
|
color = `rgb(${Math.round(r / count)}, ${Math.round(g / count)}, ${Math.round(b / count)})`;
|
||||||
|
}
|
||||||
|
return rgbToHex(color);
|
||||||
|
}
|
||||||
|
let foreColor = params.foreColor;
|
||||||
|
let hiliteColor = params.hiliteColor;
|
||||||
|
|
||||||
|
// Determine colors at cursor position
|
||||||
|
const sel = this.document.getSelection();
|
||||||
|
if (sel.rangeCount && (!foreColor || !hiliteColor)) {
|
||||||
|
const endContainer = closestElement(sel.getRangeAt(0).endContainer);
|
||||||
|
const computedStyle = getComputedStyle(endContainer);
|
||||||
|
const backgroundImage = computedStyle.backgroundImage;
|
||||||
|
const hasGradient = isColorGradient(backgroundImage);
|
||||||
|
const hasTextGradientClass = endContainer.classList.contains('text-gradient');
|
||||||
|
if (!foreColor) {
|
||||||
|
if (hasGradient && hasTextGradientClass) {
|
||||||
|
foreColor = backgroundImage;
|
||||||
|
} else {
|
||||||
|
foreColor = document.queryCommandValue('foreColor');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!hiliteColor) {
|
||||||
|
if (hasGradient && !hasTextGradientClass) {
|
||||||
|
hiliteColor = backgroundImage;
|
||||||
|
} else {
|
||||||
|
hiliteColor = computedStyle.backgroundColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// display colors in toolbar buttons
|
||||||
|
foreColor = hexFromColor(foreColor);
|
||||||
this.toolbar.style.setProperty('--fore-color', foreColor);
|
this.toolbar.style.setProperty('--fore-color', foreColor);
|
||||||
const foreColorInput = this.toolbar.querySelector('#foreColor input');
|
const foreColorInput = this.toolbar.querySelector('#foreColor input');
|
||||||
if (foreColorInput) {
|
if (foreColorInput) {
|
||||||
foreColorInput.value = foreColor;
|
foreColorInput.value = foreColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
let hiliteColor = params.hiliteColor;
|
hiliteColor = hexFromColor(hiliteColor);
|
||||||
if (!hiliteColor) {
|
|
||||||
const sel = this.document.getSelection();
|
|
||||||
if (sel.rangeCount) {
|
|
||||||
const endContainer = closestElement(sel.getRangeAt(0).endContainer);
|
|
||||||
const hiliteColorRgb = getComputedStyle(endContainer).backgroundColor;
|
|
||||||
hiliteColor = rgbToHex(hiliteColorRgb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.toolbar.style.setProperty('--hilite-color', hiliteColor);
|
this.toolbar.style.setProperty('--hilite-color', hiliteColor);
|
||||||
const hiliteColorInput = this.toolbar.querySelector('#hiliteColor input');
|
const hiliteColorInput = this.toolbar.querySelector('#hiliteColor input');
|
||||||
if (hiliteColorInput) {
|
if (hiliteColorInput) {
|
||||||
hiliteColorInput.value = hiliteColor.length <= 7 ? hiliteColor : rgbToHex(hiliteColor);
|
hiliteColorInput.value = hiliteColor.length <= 7 ? hiliteColor : hexFromColor(hiliteColor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
insertText,
|
insertText,
|
||||||
isBlock,
|
isBlock,
|
||||||
isBold,
|
isBold,
|
||||||
|
isColorGradient,
|
||||||
isContentTextNode,
|
isContentTextNode,
|
||||||
isShrunkBlock,
|
isShrunkBlock,
|
||||||
isVisible,
|
isVisible,
|
||||||
@@ -138,11 +139,25 @@ function align(editor, mode) {
|
|||||||
function colorElement(element, color, mode) {
|
function colorElement(element, color, mode) {
|
||||||
const newClassName = element.className
|
const newClassName = element.className
|
||||||
.replace(mode === 'color' ? TEXT_CLASSES_REGEX : BG_CLASSES_REGEX, '')
|
.replace(mode === 'color' ? TEXT_CLASSES_REGEX : BG_CLASSES_REGEX, '')
|
||||||
|
.replace(/\btext-gradient\b/g, '') // cannot be combined with setting a background
|
||||||
.replace(/\s+/, ' ');
|
.replace(/\s+/, ' ');
|
||||||
element.className !== newClassName && (element.className = newClassName);
|
element.className !== newClassName && (element.className = newClassName);
|
||||||
|
element.style['background-image'] = '';
|
||||||
|
if (mode === 'backgroundColor') {
|
||||||
|
element.style['background'] = '';
|
||||||
|
}
|
||||||
if (color.startsWith('text') || color.startsWith('bg-')) {
|
if (color.startsWith('text') || color.startsWith('bg-')) {
|
||||||
element.style[mode] = '';
|
element.style[mode] = '';
|
||||||
element.className += ' ' + color;
|
element.classList.add(color);
|
||||||
|
} else if (isColorGradient(color)) {
|
||||||
|
element.style[mode] = '';
|
||||||
|
if (mode === 'color') {
|
||||||
|
element.style['background'] = '';
|
||||||
|
element.style['background-image'] = color;
|
||||||
|
element.classList.add('text-gradient');
|
||||||
|
} else {
|
||||||
|
element.style['background-image'] = color;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
element.style[mode] = color;
|
element.style[mode] = color;
|
||||||
}
|
}
|
||||||
@@ -160,6 +175,17 @@ function hasColor(element, mode) {
|
|||||||
const style = element.style;
|
const style = element.style;
|
||||||
const parent = element.parentNode;
|
const parent = element.parentNode;
|
||||||
const classRegex = mode === 'color' ? TEXT_CLASSES_REGEX : BG_CLASSES_REGEX;
|
const classRegex = mode === 'color' ? TEXT_CLASSES_REGEX : BG_CLASSES_REGEX;
|
||||||
|
if (isColorGradient(style['background-image'])) {
|
||||||
|
if (element.classList.contains('text-gradient')) {
|
||||||
|
if (mode === 'color') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (mode !== 'color') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
(style[mode] && style[mode] !== 'inherit' && style[mode] !== parent.style[mode]) ||
|
(style[mode] && style[mode] !== 'inherit' && style[mode] !== parent.style[mode]) ||
|
||||||
(classRegex.test(element.className) &&
|
(classRegex.test(element.className) &&
|
||||||
|
|||||||
@@ -1216,6 +1216,15 @@ export function isShrunkBlock(blockEl) {
|
|||||||
return isEmptyBlock(blockEl) && !blockEl.querySelector('br');
|
return isEmptyBlock(blockEl) && !blockEl.querySelector('br');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} [value]
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
export function isColorGradient(value) {
|
||||||
|
// FIXME duplicated in @web_editor/utils.js
|
||||||
|
return value && value.includes('-gradient(');
|
||||||
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// DOM Modification
|
// DOM Modification
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -159,6 +159,31 @@ function _areCssValuesEqual(value1, value2, cssProp, $target) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// They may be gradients
|
||||||
|
const value1IsGradient = _isColorGradient(value1);
|
||||||
|
const value2IsGradient = _isColorGradient(value2);
|
||||||
|
if (value1IsGradient !== value2IsGradient) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (value1IsGradient) {
|
||||||
|
// Kinda hacky and probably inneficient but probably the easiest way:
|
||||||
|
// applied the value as background-image of two fakes elements and
|
||||||
|
// compare their computed value.
|
||||||
|
const temp1El = document.createElement('div');
|
||||||
|
temp1El.style.backgroundImage = value1;
|
||||||
|
document.body.appendChild(temp1El);
|
||||||
|
value1 = getComputedStyle(temp1El).backgroundImage;
|
||||||
|
document.body.removeChild(temp1El);
|
||||||
|
|
||||||
|
const temp2El = document.createElement('div');
|
||||||
|
temp2El.style.backgroundImage = value2;
|
||||||
|
document.body.appendChild(temp2El);
|
||||||
|
value2 = getComputedStyle(temp2El).backgroundImage;
|
||||||
|
document.body.removeChild(temp2El);
|
||||||
|
|
||||||
|
return value1 === value2;
|
||||||
|
}
|
||||||
|
|
||||||
// Convert the second value in the unit of the first one and compare
|
// Convert the second value in the unit of the first one and compare
|
||||||
// floating values
|
// floating values
|
||||||
const data = _getNumericAndUnit(value1);
|
const data = _getNumericAndUnit(value1);
|
||||||
@@ -237,7 +262,8 @@ function _normalizeColor(color) {
|
|||||||
* @returns {string|false} the src of the image or false if not parsable
|
* @returns {string|false} the src of the image or false if not parsable
|
||||||
*/
|
*/
|
||||||
function _getBgImageURL(el) {
|
function _getBgImageURL(el) {
|
||||||
const string = $(el).css('background-image');
|
const parts = _backgroundImageCssToParts($(el).css('background-image'));
|
||||||
|
const string = parts.url || '';
|
||||||
const match = string.match(/^url\((['"])(.*?)\1\)$/);
|
const match = string.match(/^url\((['"])(.*?)\1\)$/);
|
||||||
if (!match) {
|
if (!match) {
|
||||||
return '';
|
return '';
|
||||||
@@ -250,6 +276,47 @@ function _getBgImageURL(el) {
|
|||||||
}
|
}
|
||||||
return matchedURL;
|
return matchedURL;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Extracts url and gradient parts from the background-image CSS property.
|
||||||
|
*
|
||||||
|
* @param {string} CSS 'background-image' property value
|
||||||
|
* @returns {Object} contains the separated 'url' and 'gradient' parts
|
||||||
|
*/
|
||||||
|
function _backgroundImageCssToParts(css) {
|
||||||
|
const parts = {};
|
||||||
|
css = css || '';
|
||||||
|
if (css.startsWith('url(')) {
|
||||||
|
const urlEnd = css.indexOf(')') + 1;
|
||||||
|
parts.url = css.substring(0, urlEnd).trim();
|
||||||
|
const commaPos = css.indexOf(',', urlEnd);
|
||||||
|
css = commaPos > 0 ? css.substring(commaPos + 1) : '';
|
||||||
|
}
|
||||||
|
if (_isColorGradient(css)) {
|
||||||
|
parts.gradient = css.trim();
|
||||||
|
}
|
||||||
|
return parts;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Combines url and gradient parts into a background-image CSS property value
|
||||||
|
*
|
||||||
|
* @param {Object} contains the separated 'url' and 'gradient' parts
|
||||||
|
* @returns {string} CSS 'background-image' property value
|
||||||
|
*/
|
||||||
|
function _backgroundImagePartsToCss(parts) {
|
||||||
|
let css = parts.url || '';
|
||||||
|
if (parts.gradient) {
|
||||||
|
css += (css ? ', ' : '') + parts.gradient;
|
||||||
|
}
|
||||||
|
return css || 'none';
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param {string} [value]
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
function _isColorGradient(value) {
|
||||||
|
// FIXME duplicated in odoo-editor/utils.js
|
||||||
|
return value && value.includes('-gradient(');
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
CSS_SHORTHANDS: CSS_SHORTHANDS,
|
CSS_SHORTHANDS: CSS_SHORTHANDS,
|
||||||
@@ -261,9 +328,12 @@ return {
|
|||||||
getNumericAndUnit: _getNumericAndUnit,
|
getNumericAndUnit: _getNumericAndUnit,
|
||||||
areCssValuesEqual: _areCssValuesEqual,
|
areCssValuesEqual: _areCssValuesEqual,
|
||||||
isColorCombinationName: _isColorCombinationName,
|
isColorCombinationName: _isColorCombinationName,
|
||||||
|
isColorGradient: _isColorGradient,
|
||||||
computeColorClasses: _computeColorClasses,
|
computeColorClasses: _computeColorClasses,
|
||||||
getCSSVariableValue: _getCSSVariableValue,
|
getCSSVariableValue: _getCSSVariableValue,
|
||||||
normalizeColor: _normalizeColor,
|
normalizeColor: _normalizeColor,
|
||||||
getBgImageURL: _getBgImageURL,
|
getBgImageURL: _getBgImageURL,
|
||||||
|
backgroundImageCssToParts: _backgroundImageCssToParts,
|
||||||
|
backgroundImagePartsToCss: _backgroundImagePartsToCss,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ const weUtils = require('web_editor.utils');
|
|||||||
const {
|
const {
|
||||||
normalizeColor,
|
normalizeColor,
|
||||||
getBgImageURL,
|
getBgImageURL,
|
||||||
|
backgroundImageCssToParts,
|
||||||
|
backgroundImagePartsToCss,
|
||||||
DEFAULT_PALETTE,
|
DEFAULT_PALETTE,
|
||||||
} = weUtils;
|
} = weUtils;
|
||||||
var weWidgets = require('wysiwyg.widgets');
|
var weWidgets = require('wysiwyg.widgets');
|
||||||
@@ -480,6 +482,13 @@ const UserValueWidget = Widget.extend({
|
|||||||
const uniqArr = arr.filter((v, i, arr) => i === arr.indexOf(v));
|
const uniqArr = arr.filter((v, i, arr) => i === arr.indexOf(v));
|
||||||
this._methodsParams.optionsPossibleValues[methodName] = uniqArr;
|
this._methodsParams.optionsPossibleValues[methodName] = uniqArr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Method names come from the widget's dataset whose keys' order cannot
|
||||||
|
// be relied on. We explicitely sort them by alphabetical order allowing
|
||||||
|
// consistent behavior, while relying on order for such methods should
|
||||||
|
// not be done when possible (the methods should be independent from
|
||||||
|
// each other when possible).
|
||||||
|
this._methodsNames.sort();
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @param {boolean} [previewMode=false]
|
* @param {boolean} [previewMode=false]
|
||||||
@@ -1329,7 +1338,7 @@ const ColorpickerUserValueWidget = SelectUserValueWidget.extend({
|
|||||||
*/
|
*/
|
||||||
open: function () {
|
open: function () {
|
||||||
if (this.colorPalette.setSelectedColor) {
|
if (this.colorPalette.setSelectedColor) {
|
||||||
this.colorPalette.setSelectedColor(this._value);
|
this.colorPalette.setSelectedColor(this._ccValue, this._value);
|
||||||
} else {
|
} else {
|
||||||
// TODO review in master, this does async stuff. Maybe the open
|
// TODO review in master, this does async stuff. Maybe the open
|
||||||
// method should now be async. This is not really robust as the
|
// method should now be async. This is not really robust as the
|
||||||
@@ -1363,14 +1372,23 @@ const ColorpickerUserValueWidget = SelectUserValueWidget.extend({
|
|||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
getValue: function (methodName) {
|
getValue: function (methodName) {
|
||||||
if (typeof this._previewColor === 'string') {
|
const isCCMethod = (this._methodsParams.withCombinations === methodName);
|
||||||
return this._previewColor;
|
|
||||||
}
|
|
||||||
if (typeof this._customColorValue === 'string') {
|
|
||||||
return this._customColorValue;
|
|
||||||
}
|
|
||||||
let value = this._super(...arguments);
|
let value = this._super(...arguments);
|
||||||
|
if (isCCMethod) {
|
||||||
|
value = this._ccValue;
|
||||||
|
} else if (typeof this._customColorValue === 'string') {
|
||||||
|
value = this._customColorValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO strange there is some processing below for the normal value but
|
||||||
|
// not for the preview value? To check in older stable versions as well.
|
||||||
|
if (typeof this._previewColor === 'string') {
|
||||||
|
return isCCMethod ? this._previewCC : this._previewColor;
|
||||||
|
}
|
||||||
|
|
||||||
if (value) {
|
if (value) {
|
||||||
|
// TODO probably something to be done to handle gradients properly
|
||||||
|
// in this code.
|
||||||
const useCssColor = this.options.dataAttributes.hasOwnProperty('useCssColor');
|
const useCssColor = this.options.dataAttributes.hasOwnProperty('useCssColor');
|
||||||
const cssCompatible = this.options.dataAttributes.hasOwnProperty('cssCompatible');
|
const cssCompatible = this.options.dataAttributes.hasOwnProperty('cssCompatible');
|
||||||
if ((useCssColor || cssCompatible) && !ColorpickerWidget.isCSSColor(value)) {
|
if ((useCssColor || cssCompatible) && !ColorpickerWidget.isCSSColor(value)) {
|
||||||
@@ -1393,34 +1411,46 @@ const ColorpickerUserValueWidget = SelectUserValueWidget.extend({
|
|||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
isActive: function () {
|
isActive: function () {
|
||||||
return !weUtils.areCssValuesEqual(this._value, 'rgba(0, 0, 0, 0)');
|
return !!this._ccValue
|
||||||
|
|| !weUtils.areCssValuesEqual(this._value, 'rgba(0, 0, 0, 0)');
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Updates the color preview + re-render the whole color palette widget.
|
* Updates the color preview + re-render the whole color palette widget.
|
||||||
*
|
*
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
async setValue(color) {
|
async setValue(color, methodName, ...rest) {
|
||||||
await this._super(...arguments);
|
// The colorpicker widget can hold two values: a color combination and
|
||||||
|
// a normal color or a gradient. The base `_value` will hold the normal
|
||||||
|
// color or the gradient value. The color combination one will be
|
||||||
|
// available in `_ccValue`.
|
||||||
|
const isCCMethod = (this._methodsParams.withCombinations === methodName);
|
||||||
|
// Always call _super but don't change _value if meant for the CC value.
|
||||||
|
await this._super(isCCMethod ? this._value : color, methodName, ...rest);
|
||||||
|
if (isCCMethod) {
|
||||||
|
this._ccValue = color;
|
||||||
|
}
|
||||||
|
|
||||||
await this._colorPaletteRenderPromise;
|
await this._colorPaletteRenderPromise;
|
||||||
|
|
||||||
const classes = weUtils.computeColorClasses(this.colorPalette.getColorNames());
|
const classes = weUtils.computeColorClasses(this.colorPalette.getColorNames());
|
||||||
this.colorPreviewEl.classList.remove(...classes);
|
this.colorPreviewEl.classList.remove(...classes);
|
||||||
this.colorPreviewEl.style.removeProperty('background-color');
|
this.colorPreviewEl.style.removeProperty('background-color');
|
||||||
|
this.colorPreviewEl.style.removeProperty('background-image');
|
||||||
|
if (this._ccValue) {
|
||||||
|
this.colorPreviewEl.classList.add('o_cc', `o_cc${this._ccValue}`);
|
||||||
|
}
|
||||||
if (this._value) {
|
if (this._value) {
|
||||||
if (ColorpickerWidget.isCSSColor(this._value)) {
|
if (ColorpickerWidget.isCSSColor(this._value)) {
|
||||||
this.colorPreviewEl.style.backgroundColor = this._value;
|
this.colorPreviewEl.style.backgroundColor = this._value;
|
||||||
} else if (weUtils.isColorCombinationName(this._value)) {
|
} else if (!weUtils.isColorGradient(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}`);
|
||||||
|
} else {
|
||||||
|
this.colorPreviewEl.style.backgroundImage = this._value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
// Private
|
// Private
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
@@ -1431,14 +1461,21 @@ const ColorpickerUserValueWidget = SelectUserValueWidget.extend({
|
|||||||
*/
|
*/
|
||||||
_renderColorPalette: function () {
|
_renderColorPalette: function () {
|
||||||
const options = {
|
const options = {
|
||||||
|
selectedCC: this._ccValue,
|
||||||
selectedColor: this._value,
|
selectedColor: this._value,
|
||||||
};
|
};
|
||||||
if (this.options.dataAttributes.excluded) {
|
if (this.options.dataAttributes.excluded) {
|
||||||
options.excluded = this.options.dataAttributes.excluded.replace(/ /g, '').split(',');
|
options.excluded = this.options.dataAttributes.excluded.replace(/ /g, '').split(',');
|
||||||
}
|
}
|
||||||
|
if (this.options.dataAttributes.opacity) {
|
||||||
|
options.opacity = this.options.dataAttributes.opacity;
|
||||||
|
}
|
||||||
if (this.options.dataAttributes.withCombinations) {
|
if (this.options.dataAttributes.withCombinations) {
|
||||||
options.withCombinations = !!this.options.dataAttributes.withCombinations;
|
options.withCombinations = !!this.options.dataAttributes.withCombinations;
|
||||||
}
|
}
|
||||||
|
if (this.options.dataAttributes.withGradients) {
|
||||||
|
options.withGradients = !!this.options.dataAttributes.withGradients;
|
||||||
|
}
|
||||||
if (this.options.dataAttributes.selectedTab) {
|
if (this.options.dataAttributes.selectedTab) {
|
||||||
options.selectedTab = this.options.dataAttributes.selectedTab;
|
options.selectedTab = this.options.dataAttributes.selectedTab;
|
||||||
}
|
}
|
||||||
@@ -1479,9 +1516,13 @@ const ColorpickerUserValueWidget = SelectUserValueWidget.extend({
|
|||||||
* @param {Event} ev
|
* @param {Event} ev
|
||||||
*/
|
*/
|
||||||
_onColorPicked: function (ev) {
|
_onColorPicked: function (ev) {
|
||||||
|
this._previewCC = false;
|
||||||
this._previewColor = false;
|
this._previewColor = false;
|
||||||
this._customColorValue = false;
|
this._customColorValue = false;
|
||||||
|
|
||||||
|
this._ccValue = ev.data.ccValue;
|
||||||
this._value = ev.data.color;
|
this._value = ev.data.color;
|
||||||
|
|
||||||
this._onUserValueChange(ev);
|
this._onUserValueChange(ev);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@@ -1491,6 +1532,7 @@ const ColorpickerUserValueWidget = SelectUserValueWidget.extend({
|
|||||||
* @param {Event} ev
|
* @param {Event} ev
|
||||||
*/
|
*/
|
||||||
_onColorHovered: function (ev) {
|
_onColorHovered: function (ev) {
|
||||||
|
this._previewCC = ev.data.ccValue;
|
||||||
this._previewColor = ev.data.color;
|
this._previewColor = ev.data.color;
|
||||||
this._onUserValuePreview(ev);
|
this._onUserValuePreview(ev);
|
||||||
},
|
},
|
||||||
@@ -1501,6 +1543,7 @@ const ColorpickerUserValueWidget = SelectUserValueWidget.extend({
|
|||||||
* @param {Event} ev
|
* @param {Event} ev
|
||||||
*/
|
*/
|
||||||
_onColorLeft: function (ev) {
|
_onColorLeft: function (ev) {
|
||||||
|
this._previewCC = false;
|
||||||
this._previewColor = false;
|
this._previewColor = false;
|
||||||
this._onUserValueReset(ev);
|
this._onUserValueReset(ev);
|
||||||
},
|
},
|
||||||
@@ -3031,31 +3074,42 @@ const SnippetOptionWidget = Widget.extend({
|
|||||||
this.$target.trigger('background-color-event', previewMode);
|
this.$target.trigger('background-color-event', previewMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
const cssProps = weUtils.CSS_SHORTHANDS[params.cssProperty] || [params.cssProperty];
|
// Always reset the inline style first to not put inline style on an
|
||||||
|
// element which already have this style through css stylesheets.
|
||||||
|
let cssProps = weUtils.CSS_SHORTHANDS[params.cssProperty] || [params.cssProperty];
|
||||||
for (const cssProp of cssProps) {
|
for (const cssProp of cssProps) {
|
||||||
// Always reset the inline style first to not put inline style on an
|
|
||||||
// element which already have this style through css stylesheets.
|
|
||||||
this.$target[0].style.setProperty(cssProp, '');
|
this.$target[0].style.setProperty(cssProp, '');
|
||||||
}
|
}
|
||||||
if (params.extraClass) {
|
if (params.extraClass) {
|
||||||
this.$target.removeClass(params.extraClass);
|
this.$target.removeClass(params.extraClass);
|
||||||
}
|
}
|
||||||
|
// Plain color and gradient are mutually exclusive as background so in
|
||||||
|
// case we edit a background-color we also have to reset the gradient
|
||||||
|
// part of the background-image property (the opposite is handled by the
|
||||||
|
// fact that editing a gradient as background is done by calling this
|
||||||
|
// method with background-color as property too, so it is automatically
|
||||||
|
// reset anyway).
|
||||||
|
let bgImageParts = undefined;
|
||||||
|
if (params.cssProperty === 'background-color') {
|
||||||
|
const styles = getComputedStyle(this.$target[0]);
|
||||||
|
bgImageParts = backgroundImageCssToParts(styles['background-image']);
|
||||||
|
delete bgImageParts.gradient;
|
||||||
|
const combined = backgroundImagePartsToCss(bgImageParts);
|
||||||
|
this.$target[0].style.setProperty('background-image', '');
|
||||||
|
applyCSS.call(this, 'background-image', combined, styles);
|
||||||
|
}
|
||||||
|
|
||||||
// Only allow to use a color name as a className if we know about the
|
// Only allow to use a color name as a className if we know about the
|
||||||
// other potential color names (to remove) and if we know about a prefix
|
// other potential color names (to remove) and if we know about a prefix
|
||||||
// (otherwise we suppose that we should use the actual related color).
|
// (otherwise we suppose that we should use the actual related color).
|
||||||
|
// Note: color combinations classes are handled by a dedicated method,
|
||||||
|
// as they can be combined with normal classes.
|
||||||
if (params.colorNames && params.colorPrefix) {
|
if (params.colorNames && params.colorPrefix) {
|
||||||
const classes = weUtils.computeColorClasses(params.colorNames, params.colorPrefix);
|
const colorNames = params.colorNames.filter(name => !weUtils.isColorCombinationName(name));
|
||||||
|
const classes = weUtils.computeColorClasses(colorNames, params.colorPrefix);
|
||||||
this.$target[0].classList.remove(...classes);
|
this.$target[0].classList.remove(...classes);
|
||||||
|
|
||||||
if (weUtils.isColorCombinationName(widgetValue)) {
|
if (colorNames.includes(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);
|
|
||||||
_restoreTransitions();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (params.colorNames.includes(widgetValue)) {
|
|
||||||
const originalCSSValue = window.getComputedStyle(this.$target[0])[cssProps[0]];
|
const originalCSSValue = window.getComputedStyle(this.$target[0])[cssProps[0]];
|
||||||
const className = params.colorPrefix + widgetValue;
|
const className = params.colorPrefix + widgetValue;
|
||||||
this.$target[0].classList.add(className);
|
this.$target[0].classList.add(className);
|
||||||
@@ -3074,6 +3128,8 @@ const SnippetOptionWidget = Widget.extend({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const styles = window.getComputedStyle(this.$target[0]);
|
||||||
|
|
||||||
// At this point, the widget value is either a property/color name or
|
// At this point, the widget value is either a property/color name or
|
||||||
// an actual css property value. If it is a property/color name, we will
|
// an actual css property value. If it is a property/color name, we will
|
||||||
// apply a css variable as style value.
|
// apply a css variable as style value.
|
||||||
@@ -3082,6 +3138,21 @@ const SnippetOptionWidget = Widget.extend({
|
|||||||
widgetValue = `var(--${widgetValue})`;
|
widgetValue = `var(--${widgetValue})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// In case of background-color edition, we could receive a gradient, in
|
||||||
|
// which case the value has to be combined with the potential background
|
||||||
|
// image (real image).
|
||||||
|
if (params.cssProperty === 'background-color' && weUtils.isColorGradient(widgetValue)) {
|
||||||
|
cssProps = ['background-image'];
|
||||||
|
bgImageParts.gradient = widgetValue;
|
||||||
|
widgetValue = backgroundImagePartsToCss(bgImageParts);
|
||||||
|
|
||||||
|
// Also force the background-color to transparent as otherwise it
|
||||||
|
// won't act as a "gradient replacing the color combination
|
||||||
|
// background" but be applied over it (which would be the opposite
|
||||||
|
// of what happens when editing the background color).
|
||||||
|
applyCSS.call(this, 'background-color', 'rgba(0, 0, 0, 0)', styles);
|
||||||
|
}
|
||||||
|
|
||||||
// replacing ', ' by ',' to prevent attributes with internal space separators from being split:
|
// replacing ', ' by ',' to prevent attributes with internal space separators from being split:
|
||||||
// eg: "rgba(55, 12, 47, 1.9) 47px" should be split as ["rgba(55,12,47,1.9)", "47px"]
|
// eg: "rgba(55, 12, 47, 1.9) 47px" should be split as ["rgba(55,12,47,1.9)", "47px"]
|
||||||
const values = widgetValue.replace(/,\s/g, ',').split(/\s+/g);
|
const values = widgetValue.replace(/,\s/g, ',').split(/\s+/g);
|
||||||
@@ -3102,7 +3173,6 @@ const SnippetOptionWidget = Widget.extend({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = window.getComputedStyle(this.$target[0]);
|
|
||||||
let hasUserValue = false;
|
let hasUserValue = false;
|
||||||
for (let i = cssProps.length - 1; i > 0; i--) {
|
for (let i = cssProps.length - 1; i > 0; i--) {
|
||||||
hasUserValue = applyCSS.call(this, cssProps[i], values.pop(), styles) || hasUserValue;
|
hasUserValue = applyCSS.call(this, cssProps[i], values.pop(), styles) || hasUserValue;
|
||||||
@@ -3123,6 +3193,22 @@ const SnippetOptionWidget = Widget.extend({
|
|||||||
|
|
||||||
_restoreTransitions();
|
_restoreTransitions();
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Sets a color combination.
|
||||||
|
*
|
||||||
|
* @see this.selectClass for parameters
|
||||||
|
*/
|
||||||
|
async selectColorCombination(previewMode, widgetValue, params) {
|
||||||
|
if (params.colorNames) {
|
||||||
|
const names = params.colorNames.filter(weUtils.isColorCombinationName);
|
||||||
|
const classes = weUtils.computeColorClasses(names);
|
||||||
|
this.$target[0].classList.remove(...classes);
|
||||||
|
|
||||||
|
if (widgetValue) {
|
||||||
|
this.$target[0].classList.add('o_cc', `o_cc${widgetValue}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
// Public
|
// Public
|
||||||
@@ -3398,10 +3484,15 @@ const SnippetOptionWidget = Widget.extend({
|
|||||||
return attrValue || params.attributeDefaultValue || '';
|
return attrValue || params.attributeDefaultValue || '';
|
||||||
}
|
}
|
||||||
case 'selectStyle': {
|
case 'selectStyle': {
|
||||||
|
let usedCC = undefined;
|
||||||
if (params.colorPrefix && params.colorNames) {
|
if (params.colorPrefix && params.colorNames) {
|
||||||
for (const c of params.colorNames) {
|
for (const c of params.colorNames) {
|
||||||
const className = weUtils.computeColorClasses([c], params.colorPrefix)[0];
|
const className = weUtils.computeColorClasses([c], params.colorPrefix)[0];
|
||||||
if (this.$target[0].classList.contains(className)) {
|
if (this.$target[0].classList.contains(className)) {
|
||||||
|
if (weUtils.isColorCombinationName(c)) {
|
||||||
|
usedCC = c;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3414,6 +3505,18 @@ const SnippetOptionWidget = Widget.extend({
|
|||||||
const _restoreTransitions = () => this.$target[0].classList.remove('o_we_force_no_transition');
|
const _restoreTransitions = () => this.$target[0].classList.remove('o_we_force_no_transition');
|
||||||
|
|
||||||
const styles = window.getComputedStyle(this.$target[0]);
|
const styles = window.getComputedStyle(this.$target[0]);
|
||||||
|
|
||||||
|
if (params.cssProperty === 'background-color') {
|
||||||
|
// Check if there is a gradient, in that case this is the
|
||||||
|
// value to be returned, we normally not allow color and
|
||||||
|
// gradient at the same time (the option would remove one
|
||||||
|
// if editing the other).
|
||||||
|
const parts = backgroundImageCssToParts(styles['background-image']);
|
||||||
|
if (parts.gradient) {
|
||||||
|
return parts.gradient;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const cssProps = weUtils.CSS_SHORTHANDS[params.cssProperty] || [params.cssProperty];
|
const cssProps = weUtils.CSS_SHORTHANDS[params.cssProperty] || [params.cssProperty];
|
||||||
const cssValues = cssProps.map(cssProp => {
|
const cssValues = cssProps.map(cssProp => {
|
||||||
let value = styles[cssProp].trim();
|
let value = styles[cssProp].trim();
|
||||||
@@ -3438,7 +3541,44 @@ const SnippetOptionWidget = Widget.extend({
|
|||||||
|
|
||||||
_restoreTransitions();
|
_restoreTransitions();
|
||||||
|
|
||||||
return cssValues.join(' ');
|
const value = cssValues.join(' ');
|
||||||
|
|
||||||
|
if (params.cssProperty === 'background-color' && params.withCombinations) {
|
||||||
|
if (usedCC) {
|
||||||
|
const ccValue = weUtils.getCSSVariableValue(`o-cc${usedCC}-bg`).trim();
|
||||||
|
if (weUtils.areCssValuesEqual(value, ccValue)) {
|
||||||
|
// Prevent to consider that a color is used as CC
|
||||||
|
// override in case that color is the same as the
|
||||||
|
// one used in that CC.
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const rgba = ColorpickerWidget.convertCSSColorToRgba(value);
|
||||||
|
if (rgba && rgba.opacity < 0.001) {
|
||||||
|
// Prevent to consider a transparent color is
|
||||||
|
// applied as background unless it is to override a
|
||||||
|
// CC. Simply allows to add a CC on a transparent
|
||||||
|
// snippet in the first place.
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
case 'selectColorCombination': {
|
||||||
|
if (params.colorNames) {
|
||||||
|
for (const c of params.colorNames) {
|
||||||
|
if (!weUtils.isColorCombinationName(c)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const className = weUtils.computeColorClasses([c])[0];
|
||||||
|
if (this.$target[0].classList.contains(className)) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -5038,7 +5178,10 @@ registry.BackgroundOptimize = ImageHandlerOption.extend({
|
|||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
_applyImage(img) {
|
_applyImage(img) {
|
||||||
this.$target.css('background-image', `url('${img.getAttribute('src')}')`);
|
const parts = backgroundImageCssToParts(this.$target.css('background-image'));
|
||||||
|
parts.url = `url('${img.getAttribute('src')}')`;
|
||||||
|
const combined = backgroundImagePartsToCss(parts);
|
||||||
|
this.$target.css('background-image', combined);
|
||||||
},
|
},
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
@@ -5060,20 +5203,6 @@ registry.BackgroundOptimize = ImageHandlerOption.extend({
|
|||||||
});
|
});
|
||||||
|
|
||||||
registry.BackgroundToggler = SnippetOptionWidget.extend({
|
registry.BackgroundToggler = SnippetOptionWidget.extend({
|
||||||
/**
|
|
||||||
* @override
|
|
||||||
*/
|
|
||||||
start() {
|
|
||||||
this.$target.on('content_changed.BackgroundToggler', this._onExternalUpdate.bind(this));
|
|
||||||
return this._super(...arguments);
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* @override
|
|
||||||
*/
|
|
||||||
destroy() {
|
|
||||||
this._super(...arguments);
|
|
||||||
this.$target.off('.BackgroundToggler');
|
|
||||||
},
|
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
// Options
|
// Options
|
||||||
@@ -5086,6 +5215,7 @@ registry.BackgroundToggler = SnippetOptionWidget.extend({
|
|||||||
*/
|
*/
|
||||||
toggleBgImage(previewMode, widgetValue, params) {
|
toggleBgImage(previewMode, widgetValue, params) {
|
||||||
if (!widgetValue) {
|
if (!widgetValue) {
|
||||||
|
this.$target.find('> .o_we_bg_filter').remove();
|
||||||
// TODO: use setWidgetValue instead of calling background directly when possible
|
// TODO: use setWidgetValue instead of calling background directly when possible
|
||||||
const [bgImageWidget] = this._requestUserValueWidgets('bg_image_opt');
|
const [bgImageWidget] = this._requestUserValueWidgets('bg_image_opt');
|
||||||
const bgImageOpt = bgImageWidget.getParent();
|
const bgImageOpt = bgImageWidget.getParent();
|
||||||
@@ -5108,23 +5238,39 @@ registry.BackgroundToggler = SnippetOptionWidget.extend({
|
|||||||
return shapeOption._toggleShape();
|
return shapeOption._toggleShape();
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Toggles background filter on or off.
|
* Sets a color filter.
|
||||||
*
|
*
|
||||||
* @see this.selectClass for parameters
|
* @see this.selectClass for parameters
|
||||||
*/
|
*/
|
||||||
toggleBgFilter(previewMode, widgetValue, params) {
|
async selectFilterColor(previewMode, widgetValue, params) {
|
||||||
if (widgetValue) {
|
// Find the filter element.
|
||||||
const bgFilterEl = document.createElement('div');
|
let filterEl = this.$target[0].querySelector(':scope > .o_we_bg_filter');
|
||||||
bgFilterEl.classList.add('o_we_bg_filter', 'bg-black-50');
|
|
||||||
|
// If the filter would be transparent, remove it / don't create it.
|
||||||
|
const rgba = widgetValue && ColorpickerWidget.convertCSSColorToRgba(widgetValue);
|
||||||
|
if (!widgetValue || rgba && rgba.opacity < 0.001) {
|
||||||
|
if (filterEl) {
|
||||||
|
filterEl.remove();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the filter if necessary.
|
||||||
|
if (!filterEl) {
|
||||||
|
filterEl = document.createElement('div');
|
||||||
|
filterEl.classList.add('o_we_bg_filter');
|
||||||
const lastBackgroundEl = this._getLastPreFilterLayerElement();
|
const lastBackgroundEl = this._getLastPreFilterLayerElement();
|
||||||
if (lastBackgroundEl) {
|
if (lastBackgroundEl) {
|
||||||
$(lastBackgroundEl).after(bgFilterEl);
|
$(lastBackgroundEl).after(filterEl);
|
||||||
} else {
|
} else {
|
||||||
this.$target.prepend(bgFilterEl);
|
this.$target.prepend(filterEl);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
this.$target.find('.o_we_bg_filter').remove();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply the color on the filter.
|
||||||
|
const obj = createPropertyProxy(this, '$target', $(filterEl));
|
||||||
|
params.cssProperty = 'background-color';
|
||||||
|
return this.selectStyle.call(obj, previewMode, widgetValue, params);
|
||||||
},
|
},
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
@@ -5141,14 +5287,20 @@ registry.BackgroundToggler = SnippetOptionWidget.extend({
|
|||||||
const bgImageOpt = bgImageWidget.getParent();
|
const bgImageOpt = bgImageWidget.getParent();
|
||||||
return !!bgImageOpt._computeWidgetState('background', bgImageWidget.getMethodsParams('background'));
|
return !!bgImageOpt._computeWidgetState('background', bgImageWidget.getMethodsParams('background'));
|
||||||
}
|
}
|
||||||
case 'toggleBgFilter': {
|
|
||||||
return this._hasBgFilter();
|
|
||||||
}
|
|
||||||
case 'toggleBgShape': {
|
case 'toggleBgShape': {
|
||||||
const [shapeWidget] = this._requestUserValueWidgets('bg_shape_opt');
|
const [shapeWidget] = this._requestUserValueWidgets('bg_shape_opt');
|
||||||
const shapeOption = shapeWidget.getParent();
|
const shapeOption = shapeWidget.getParent();
|
||||||
return !!shapeOption._computeWidgetState('shape', shapeWidget.getMethodsParams('shape'));
|
return !!shapeOption._computeWidgetState('shape', shapeWidget.getMethodsParams('shape'));
|
||||||
}
|
}
|
||||||
|
case 'selectFilterColor': {
|
||||||
|
const filterEl = this.$target[0].querySelector(':scope > .o_we_bg_filter');
|
||||||
|
if (!filterEl) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
const obj = createPropertyProxy(this, '$target', $(filterEl));
|
||||||
|
params.cssProperty = 'background-color';
|
||||||
|
return this._computeWidgetState.call(obj, 'selectStyle', params);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return this._super(...arguments);
|
return this._super(...arguments);
|
||||||
},
|
},
|
||||||
@@ -5158,32 +5310,6 @@ registry.BackgroundToggler = SnippetOptionWidget.extend({
|
|||||||
_getLastPreFilterLayerElement() {
|
_getLastPreFilterLayerElement() {
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
* @returns {Boolean}
|
|
||||||
*/
|
|
||||||
_hasBgFilter() {
|
|
||||||
return !!this.$target.find('> .o_we_bg_filter').length;
|
|
||||||
},
|
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
|
||||||
// Handlers
|
|
||||||
//--------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
_onExternalUpdate() {
|
|
||||||
if (this._hasBgFilter()
|
|
||||||
&& !this._getLastPreFilterLayerElement()
|
|
||||||
&& !getBgImageURL(this.$target)) {
|
|
||||||
// No 'pre-filter' background layout anymore and no more background
|
|
||||||
// image: remove the background filter option.
|
|
||||||
// TODO there probably is a better system to implement to do that
|
|
||||||
const widget = this._requestUserValueWidgets('bg_filter_toggle_opt')[0];
|
|
||||||
widget.enable();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -5235,14 +5361,14 @@ registry.BackgroundImage = SnippetOptionWidget.extend({
|
|||||||
this.previousSrc = currentSrc;
|
this.previousSrc = currentSrc;
|
||||||
break;
|
break;
|
||||||
case 'reset':
|
case 'reset':
|
||||||
this.$target.css('background-image', `url('${this.previousSrc}')`);
|
this._setBackground(this.previousSrc);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const newURL = new URL(currentSrc, window.location.origin);
|
const newURL = new URL(currentSrc, window.location.origin);
|
||||||
newURL.searchParams.set(params.colorName, normalizeColor(widgetValue));
|
newURL.searchParams.set(params.colorName, normalizeColor(widgetValue));
|
||||||
const src = newURL.pathname + newURL.search;
|
const src = newURL.pathname + newURL.search;
|
||||||
await loadImage(src);
|
await loadImage(src);
|
||||||
this.$target.css('background-image', `url('${src}')`);
|
this._setBackground(src);
|
||||||
if (!previewMode) {
|
if (!previewMode) {
|
||||||
this.previousSrc = src;
|
this.previousSrc = src;
|
||||||
}
|
}
|
||||||
@@ -5313,13 +5439,16 @@ registry.BackgroundImage = SnippetOptionWidget.extend({
|
|||||||
* @param {string} backgroundURL
|
* @param {string} backgroundURL
|
||||||
*/
|
*/
|
||||||
_setBackground(backgroundURL) {
|
_setBackground(backgroundURL) {
|
||||||
|
const parts = backgroundImageCssToParts(this.$target.css('background-image'));
|
||||||
if (backgroundURL) {
|
if (backgroundURL) {
|
||||||
this.$target.css('background-image', `url('${backgroundURL}')`);
|
parts.url = `url('${backgroundURL}')`;
|
||||||
this.$target.addClass('oe_img_bg o_bg_img_center');
|
this.$target.addClass('oe_img_bg o_bg_img_center');
|
||||||
} else {
|
} else {
|
||||||
this.$target.css('background-image', '');
|
delete parts.url;
|
||||||
this.$target.removeClass('oe_img_bg o_bg_img_center');
|
this.$target.removeClass('oe_img_bg o_bg_img_center');
|
||||||
}
|
}
|
||||||
|
const combined = backgroundImagePartsToCss(parts);
|
||||||
|
this.$target.css('background-image', combined);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,10 @@ const ColorPaletteWidget = Widget.extend({
|
|||||||
* @param {boolean} [options.resetButton=true] Whether to display or not the reset button.
|
* @param {boolean} [options.resetButton=true] Whether to display or not the reset button.
|
||||||
* @param {string[]} [options.excluded=[]] Sections not to display.
|
* @param {string[]} [options.excluded=[]] Sections not to display.
|
||||||
* @param {string[]} [options.excludeSectionOf] Extra section to exclude: the one containing the named color.
|
* @param {string[]} [options.excludeSectionOf] Extra section to exclude: the one containing the named color.
|
||||||
|
* @param {boolean} [options.withCombinations=false] Enable color combinations selection.
|
||||||
|
* @param {float} [options.opacity=1] Specify a default opacity (predefined gradients & color).
|
||||||
|
* @param {string} [options.selectedTab='theme-colors'] Tab initially selected.
|
||||||
|
* @param {boolean} [options.withGradients=false] Enable gradient selection.
|
||||||
* @param {JQuery} [options.$editable=$()] Editable content from which the custom colors are retrieved.
|
* @param {JQuery} [options.$editable=$()] Editable content from which the custom colors are retrieved.
|
||||||
*/
|
*/
|
||||||
init: function (parent, options) {
|
init: function (parent, options) {
|
||||||
@@ -46,9 +50,10 @@ const ColorPaletteWidget = Widget.extend({
|
|||||||
excludeSectionOf: null,
|
excludeSectionOf: null,
|
||||||
$editable: $(),
|
$editable: $(),
|
||||||
withCombinations: false,
|
withCombinations: false,
|
||||||
|
opacity: 1,
|
||||||
selectedTab: 'theme-colors',
|
selectedTab: 'theme-colors',
|
||||||
|
withGradients: false,
|
||||||
}, options || {});
|
}, options || {});
|
||||||
|
|
||||||
this.selectedColor = '';
|
this.selectedColor = '';
|
||||||
this.resetButton = this.options.resetButton;
|
this.resetButton = this.options.resetButton;
|
||||||
this.withCombinations = this.options.withCombinations;
|
this.withCombinations = this.options.withCombinations;
|
||||||
@@ -69,6 +74,10 @@ const ColorPaletteWidget = Widget.extend({
|
|||||||
'transparent_grayscale',
|
'transparent_grayscale',
|
||||||
'common_grays',
|
'common_grays',
|
||||||
],
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'gradients',
|
||||||
|
pickers: this.options.withGradients ? ['predefined_gradients'] : [],
|
||||||
}];
|
}];
|
||||||
|
|
||||||
this.sections = {};
|
this.sections = {};
|
||||||
@@ -118,15 +127,18 @@ const ColorPaletteWidget = Widget.extend({
|
|||||||
pickerEl.dataset.name = 'custom';
|
pickerEl.dataset.name = 'custom';
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
pickerEl = colorpickerEl.querySelector(`[data-name="${pickerId}"]`).cloneNode(true);
|
pickerEl = colorpickerEl.querySelector(`[data-name="${pickerId}"]`);
|
||||||
|
pickerEl = pickerEl && pickerEl.cloneNode(true);
|
||||||
}
|
}
|
||||||
sectionEl.appendChild(pickerEl);
|
if (pickerEl) {
|
||||||
|
sectionEl.appendChild(pickerEl);
|
||||||
|
|
||||||
if (!this.options.excluded.includes(pickerId)) {
|
if (!this.options.excluded.includes(pickerId)) {
|
||||||
sectionIsEmpty = false;
|
sectionIsEmpty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.pickers[pickerId] = pickerEl;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.pickers[pickerId] = pickerEl;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// If the section is empty, hide it and
|
// If the section is empty, hide it and
|
||||||
@@ -141,6 +153,16 @@ const ColorPaletteWidget = Widget.extend({
|
|||||||
this.sections[tab.id] = sectionEl;
|
this.sections[tab.id] = sectionEl;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Predefined gradient opacity
|
||||||
|
if (this.options.withGradients && this.options.opacity !== 1) {
|
||||||
|
this.pickers['predefined_gradients'].querySelectorAll('button').forEach(elem => {
|
||||||
|
let gradient = elem.dataset.color;
|
||||||
|
gradient = gradient.replaceAll(/rgba?(\(\s*\d+\s*,\s*\d+\s*,\s*\d+)(?:\s*,.+?)?\)/g,
|
||||||
|
`rgba$1, ${this.options.opacity})`);
|
||||||
|
elem.dataset.color = gradient.replaceAll(/\s+/g, '');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Switch to the correct tab
|
// Switch to the correct tab
|
||||||
const selectedButtonIndex = this.tabs.map(tab => tab.id).indexOf(this.options.selectedTab);
|
const selectedButtonIndex = this.tabs.map(tab => tab.id).indexOf(this.options.selectedTab);
|
||||||
this._selectTabFromButton(this.el.querySelectorAll('button')[selectedButtonIndex]);
|
this._selectTabFromButton(this.el.querySelectorAll('button')[selectedButtonIndex]);
|
||||||
@@ -183,6 +205,9 @@ const ColorPaletteWidget = Widget.extend({
|
|||||||
this.colorToColorNames = {};
|
this.colorToColorNames = {};
|
||||||
this.el.querySelectorAll('button[data-color]').forEach(elem => {
|
this.el.querySelectorAll('button[data-color]').forEach(elem => {
|
||||||
const colorName = elem.dataset.color;
|
const colorName = elem.dataset.color;
|
||||||
|
if (weUtils.isColorGradient(colorName)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const $color = $(elem);
|
const $color = $(elem);
|
||||||
const isCCName = weUtils.isColorCombinationName(colorName);
|
const isCCName = weUtils.isColorCombinationName(colorName);
|
||||||
if (isCCName) {
|
if (isCCName) {
|
||||||
@@ -199,6 +224,9 @@ const ColorPaletteWidget = Widget.extend({
|
|||||||
|
|
||||||
// Select selected Color and build customColors.
|
// Select selected Color and build customColors.
|
||||||
// If no color is selected selectedColor is an empty string (transparent is interpreted as no color)
|
// If no color is selected selectedColor is an empty string (transparent is interpreted as no color)
|
||||||
|
if (this.options.selectedCC) {
|
||||||
|
this.selectedCC = this.options.selectedCC;
|
||||||
|
}
|
||||||
if (this.options.selectedColor) {
|
if (this.options.selectedColor) {
|
||||||
let selectedColor = this.options.selectedColor;
|
let selectedColor = this.options.selectedColor;
|
||||||
if (compatibilityColorNames.includes(selectedColor)) {
|
if (compatibilityColorNames.includes(selectedColor)) {
|
||||||
@@ -218,12 +246,14 @@ const ColorPaletteWidget = Widget.extend({
|
|||||||
if (defaultColor && !ColorpickerWidget.isCSSColor(defaultColor)) {
|
if (defaultColor && !ColorpickerWidget.isCSSColor(defaultColor)) {
|
||||||
defaultColor = weUtils.getCSSVariableValue(defaultColor, this.style);
|
defaultColor = weUtils.getCSSVariableValue(defaultColor, this.style);
|
||||||
}
|
}
|
||||||
|
if (!defaultColor && this.options.opacity !== 1) {
|
||||||
|
defaultColor = 'rgba(0, 0, 0, ' + this.options.opacity + ')';
|
||||||
|
}
|
||||||
this.colorPicker = new ColorpickerWidget(this, {
|
this.colorPicker = new ColorpickerWidget(this, {
|
||||||
defaultColor: defaultColor,
|
defaultColor: defaultColor,
|
||||||
});
|
});
|
||||||
await this.colorPicker.appendTo(this.sections['custom-colors']);
|
await this.colorPicker.appendTo(this.sections['custom-colors']);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@@ -233,12 +263,33 @@ const ColorPaletteWidget = Widget.extend({
|
|||||||
return this.colorNames;
|
return this.colorNames;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Sets the currently selected color
|
* Gets the currently selected colors.
|
||||||
*
|
*
|
||||||
|
* @returns {Object} ccValue and color (plain color or gradient).
|
||||||
|
*/
|
||||||
|
getSelectedColors() {
|
||||||
|
return {
|
||||||
|
ccValue: this.selectedCC,
|
||||||
|
color: this.selectedColor,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Sets the currently selected colors
|
||||||
|
*
|
||||||
|
* @param {string|number} ccValue
|
||||||
* @param {string} color rgb[a]
|
* @param {string} color rgb[a]
|
||||||
*/
|
*/
|
||||||
setSelectedColor: function (color) {
|
setSelectedColor: function (ccValue, color) {
|
||||||
this._selectColor({color: color});
|
if (color === 'rgba(0, 0, 0, 0)' && this.options.opacity !== 1) {
|
||||||
|
color = 'rgba(0, 0, 0, ' + this.options.opacity + ')';
|
||||||
|
}
|
||||||
|
this._selectColor({
|
||||||
|
ccValue: ccValue,
|
||||||
|
color: color,
|
||||||
|
});
|
||||||
|
// This is called on open, restore default tab selection
|
||||||
|
const selectedButtonIndex = this.tabs.map(tab => tab.id).indexOf(this.options.selectedTab);
|
||||||
|
this._selectTabFromButton(this.el.querySelectorAll('button')[selectedButtonIndex]);
|
||||||
},
|
},
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
@@ -330,18 +381,30 @@ const ColorPaletteWidget = Widget.extend({
|
|||||||
*/
|
*/
|
||||||
_getButtonInfo: function (buttonEl) {
|
_getButtonInfo: function (buttonEl) {
|
||||||
const bgColor = buttonEl.style.backgroundColor;
|
const bgColor = buttonEl.style.backgroundColor;
|
||||||
return {
|
const value = buttonEl.dataset.color || (bgColor && bgColor !== 'initial' ? ColorpickerWidget.normalizeCSSColor(bgColor) : '') || '';
|
||||||
color: bgColor ? ColorpickerWidget.normalizeCSSColor(bgColor) : buttonEl.dataset.color || '',
|
const info = {
|
||||||
target: buttonEl,
|
target: buttonEl,
|
||||||
};
|
};
|
||||||
|
if (!value) {
|
||||||
|
info.ccValue = '';
|
||||||
|
info.color = '';
|
||||||
|
} else if (weUtils.isColorCombinationName(value)) {
|
||||||
|
info.ccValue = value;
|
||||||
|
} else {
|
||||||
|
info.color = value;
|
||||||
|
}
|
||||||
|
return info;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Set the selectedColor and trigger an event
|
* Set the selectedColor and trigger an event
|
||||||
*
|
*
|
||||||
* @param {Object} color
|
* @param {Object} colorInfo
|
||||||
|
* @param {string} [colorInfo.ccValue]
|
||||||
|
* @param {string} [colorInfo.color]
|
||||||
* @param {string} [eventName]
|
* @param {string} [eventName]
|
||||||
*/
|
*/
|
||||||
_selectColor: function (colorInfo, eventName) {
|
_selectColor: function (colorInfo, eventName) {
|
||||||
|
this.selectedCC = colorInfo.ccValue;
|
||||||
this.selectedColor = colorInfo.color = this.colorToColorNames[colorInfo.color] || colorInfo.color;
|
this.selectedColor = colorInfo.color = this.colorToColorNames[colorInfo.color] || colorInfo.color;
|
||||||
if (eventName) {
|
if (eventName) {
|
||||||
this.trigger_up(eventName, colorInfo);
|
this.trigger_up(eventName, colorInfo);
|
||||||
@@ -353,15 +416,18 @@ const ColorPaletteWidget = Widget.extend({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Mark the selected color
|
* Marks the selected colors.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_markSelectedColor: function () {
|
_markSelectedColor: function () {
|
||||||
this.el.querySelectorAll('button.selected').forEach(el => el.classList.remove('selected'));
|
for (const buttonEl of this.el.querySelectorAll('button')) {
|
||||||
const buttonToSelectEls = this.el.querySelectorAll(`button[data-color="${this.selectedColor}"], button[style*="background-color:${this.selectedColor};"]`);
|
// TODO buttons should only be search by data-color value
|
||||||
for (const buttonToSelectEl of buttonToSelectEls) {
|
// instead of style but seems necessary for custom colors right
|
||||||
buttonToSelectEl.classList.add('selected');
|
// now...
|
||||||
|
const value = buttonEl.dataset.color || buttonEl.style.backgroundColor;
|
||||||
|
buttonEl.classList.toggle('selected', value
|
||||||
|
&& (this.selectedCC === value || weUtils.areCssValuesEqual(this.selectedColor, value)));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@@ -370,7 +436,10 @@ const ColorPaletteWidget = Widget.extend({
|
|||||||
* @private
|
* @private
|
||||||
* @param {HTMLElement} buttonEl
|
* @param {HTMLElement} buttonEl
|
||||||
*/
|
*/
|
||||||
_selectTabFromButton(buttonEl) {
|
_selectTabFromButton(buttonEl) {
|
||||||
|
this.el.querySelectorAll('.o_we_colorpicker_switch_pane_btn').forEach(el => {
|
||||||
|
el.classList.remove('active');
|
||||||
|
});
|
||||||
buttonEl.classList.add('active');
|
buttonEl.classList.add('active');
|
||||||
this.el.querySelectorAll('.o_colorpicker_sections').forEach(el => {
|
this.el.querySelectorAll('.o_colorpicker_sections').forEach(el => {
|
||||||
el.classList.toggle('d-none', el.dataset.colorTab !== buttonEl.dataset.target);
|
el.classList.toggle('d-none', el.dataset.colorTab !== buttonEl.dataset.target);
|
||||||
@@ -389,7 +458,7 @@ const ColorPaletteWidget = Widget.extend({
|
|||||||
*/
|
*/
|
||||||
_onColorButtonClick: function (ev) {
|
_onColorButtonClick: function (ev) {
|
||||||
const buttonEl = ev.currentTarget;
|
const buttonEl = ev.currentTarget;
|
||||||
const colorInfo = this._getButtonInfo(buttonEl);
|
const colorInfo = Object.assign(this.getSelectedColors(), this._getButtonInfo(buttonEl));
|
||||||
this._selectColor(colorInfo, 'color_picked');
|
this._selectColor(colorInfo, 'color_picked');
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@@ -400,7 +469,7 @@ const ColorPaletteWidget = Widget.extend({
|
|||||||
*/
|
*/
|
||||||
_onColorButtonEnter: function (ev) {
|
_onColorButtonEnter: function (ev) {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
this.trigger_up('color_hover', this._getButtonInfo(ev.currentTarget));
|
this.trigger_up('color_hover', Object.assign(this.getSelectedColors(), this._getButtonInfo(ev.currentTarget)));
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Called when a color button is left the data color is the color currently selected.
|
* Called when a color button is left the data color is the color currently selected.
|
||||||
@@ -410,10 +479,9 @@ const ColorPaletteWidget = Widget.extend({
|
|||||||
*/
|
*/
|
||||||
_onColorButtonLeave: function (ev) {
|
_onColorButtonLeave: function (ev) {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
this.trigger_up('color_leave', {
|
this.trigger_up('color_leave', Object.assign(this.getSelectedColors(), {
|
||||||
color: this.selectedColor,
|
|
||||||
target: ev.target,
|
target: ev.target,
|
||||||
});
|
}));
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Called when an update is made on the colorpicker.
|
* Called when an update is made on the colorpicker.
|
||||||
@@ -422,10 +490,10 @@ const ColorPaletteWidget = Widget.extend({
|
|||||||
* @param {Event} ev
|
* @param {Event} ev
|
||||||
*/
|
*/
|
||||||
_onColorPickerPreview: function (ev) {
|
_onColorPickerPreview: function (ev) {
|
||||||
this.trigger_up('color_hover', {
|
this.trigger_up('color_hover', Object.assign(this.getSelectedColors(), {
|
||||||
color: ev.data.cssColor,
|
color: ev.data.cssColor,
|
||||||
target: this.colorPicker.el,
|
target: this.colorPicker.el,
|
||||||
});
|
}));
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Called when a color is selected on the colorpicker (mouseup).
|
* Called when a color is selected on the colorpicker (mouseup).
|
||||||
@@ -434,10 +502,10 @@ const ColorPaletteWidget = Widget.extend({
|
|||||||
* @param {Event} ev
|
* @param {Event} ev
|
||||||
*/
|
*/
|
||||||
_onColorPickerSelect: function (ev) {
|
_onColorPickerSelect: function (ev) {
|
||||||
this._selectColor({
|
this._selectColor(Object.assign(this.getSelectedColors(), {
|
||||||
color: ev.data.cssColor,
|
color: ev.data.cssColor,
|
||||||
target: this.colorPicker.el,
|
target: this.colorPicker.el,
|
||||||
}, 'custom_color_picked');
|
}), 'custom_color_picked');
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
@@ -445,9 +513,6 @@ const ColorPaletteWidget = Widget.extend({
|
|||||||
*/
|
*/
|
||||||
_onSwitchPaneButtonClick(ev) {
|
_onSwitchPaneButtonClick(ev) {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
this.el.querySelectorAll('.o_we_colorpicker_switch_pane_btn').forEach(el => {
|
|
||||||
el.classList.remove('active');
|
|
||||||
});
|
|
||||||
this._selectTabFromButton(ev.currentTarget);
|
this._selectTabFromButton(ev.currentTarget);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ const snippetsEditor = require('web_editor.snippet.editor');
|
|||||||
const Toolbar = require('web_editor.toolbar');
|
const Toolbar = require('web_editor.toolbar');
|
||||||
const weWidgets = require('wysiwyg.widgets');
|
const weWidgets = require('wysiwyg.widgets');
|
||||||
const wysiwygUtils = require('@web_editor/js/wysiwyg/wysiwyg_utils');
|
const wysiwygUtils = require('@web_editor/js/wysiwyg/wysiwyg_utils');
|
||||||
|
const weUtils = require('web_editor.utils');
|
||||||
|
|
||||||
var _t = core._t;
|
var _t = core._t;
|
||||||
|
|
||||||
@@ -393,7 +394,10 @@ const Wysiwyg = Widget.extend({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (isBackground) {
|
if (isBackground) {
|
||||||
$(el).css('background-image', `url('${newAttachmentSrc}')`);
|
const parts = weUtils.backgroundImageCssToParts($(el).css('background-image'));
|
||||||
|
parts.url = `url('${newAttachmentSrc}')`;
|
||||||
|
const combined = weUtils.backgroundImagePartsToCss(parts);
|
||||||
|
$(el).css('background-image', combined);
|
||||||
delete el.dataset.bgSrc;
|
delete el.dataset.bgSrc;
|
||||||
} else {
|
} else {
|
||||||
el.setAttribute('src', newAttachmentSrc);
|
el.setAttribute('src', newAttachmentSrc);
|
||||||
@@ -832,10 +836,19 @@ const Wysiwyg = Widget.extend({
|
|||||||
const targetElement = targetNode && targetNode.nodeType === Node.ELEMENT_NODE
|
const targetElement = targetNode && targetNode.nodeType === Node.ELEMENT_NODE
|
||||||
? targetNode
|
? targetNode
|
||||||
: targetNode && targetNode.parentNode;
|
: targetNode && targetNode.parentNode;
|
||||||
|
const backgroundImage = $(targetElement).css('background-image');
|
||||||
|
let backgroundGradient = false;
|
||||||
|
if (weUtils.isColorGradient(backgroundImage)) {
|
||||||
|
const textGradient = targetElement.classList.contains('text-gradient');
|
||||||
|
if (eventName === "foreColor" && textGradient || eventName !== "foreColor" && !textGradient) {
|
||||||
|
backgroundGradient = backgroundImage;
|
||||||
|
}
|
||||||
|
}
|
||||||
colorpicker = new ColorPaletteWidget(this, {
|
colorpicker = new ColorPaletteWidget(this, {
|
||||||
excluded: ['transparent_grayscale'],
|
excluded: ['transparent_grayscale'],
|
||||||
$editable: $(this.odooEditor.editable), // Our parent is the root widget, we can't retrieve the editable section from it...
|
$editable: $(this.odooEditor.editable), // Our parent is the root widget, we can't retrieve the editable section from it...
|
||||||
selectedColor: $(targetElement).css(eventName === "foreColor" ? 'color' : 'backgroundColor'),
|
selectedColor: backgroundGradient || $(targetElement).css(eventName === "foreColor" ? 'color' : 'backgroundColor'),
|
||||||
|
withGradients: true,
|
||||||
});
|
});
|
||||||
colorpicker.on('custom_color_picked color_picked', null, ev => {
|
colorpicker.on('custom_color_picked color_picked', null, ev => {
|
||||||
this._processAndApplyColor(eventName, ev.data.color);
|
this._processAndApplyColor(eventName, ev.data.color);
|
||||||
@@ -869,7 +882,7 @@ const Wysiwyg = Widget.extend({
|
|||||||
_processAndApplyColor: function (eventName, color) {
|
_processAndApplyColor: function (eventName, color) {
|
||||||
if (!color) {
|
if (!color) {
|
||||||
color = 'inherit';
|
color = 'inherit';
|
||||||
} else if (!ColorpickerWidget.isCSSColor(color)) {
|
} else if (!ColorpickerWidget.isCSSColor(color) && !weUtils.isColorGradient(color)) {
|
||||||
color = (eventName === "foreColor" ? 'text-' : 'bg-') + color;
|
color = (eventName === "foreColor" ? 'text-' : 'bg-') + color;
|
||||||
}
|
}
|
||||||
this.odooEditor.execCommand('applyColor', color, eventName === 'foreColor' ? 'color' : 'backgroundColor', this.lastMediaClicked);
|
this.odooEditor.execCommand('applyColor', color, eventName === 'foreColor' ? 'color' : 'backgroundColor', this.lastMediaClicked);
|
||||||
@@ -881,6 +894,9 @@ const Wysiwyg = Widget.extend({
|
|||||||
_colorToHex: function (color) {
|
_colorToHex: function (color) {
|
||||||
if (color.startsWith('#')) {
|
if (color.startsWith('#')) {
|
||||||
return color;
|
return color;
|
||||||
|
} else if (weUtils.isColorGradient(color)) {
|
||||||
|
// return gradient the way it is: updateColorpickerLabels will handle it
|
||||||
|
return color;
|
||||||
} else {
|
} else {
|
||||||
let rgbColor;
|
let rgbColor;
|
||||||
if (color.startsWith('rgb')) {
|
if (color.startsWith('rgb')) {
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ $o-color-palette: map-get($o-color-palettes, $o-color-palette-name) or ();
|
|||||||
// Original color palette can contain override of the default combinations (so keep 'null' values for this merge)
|
// Original color palette can contain override of the default combinations (so keep 'null' values for this merge)
|
||||||
$o-color-palette: map-merge($-combination-additions, $o-color-palette);
|
$o-color-palette: map-merge($-combination-additions, $o-color-palette);
|
||||||
$o-color-palette: map-merge($o-base-color-palette, o-map-omit($o-color-palette));
|
$o-color-palette: map-merge($o-base-color-palette, o-map-omit($o-color-palette));
|
||||||
|
$o-color-palette: o-map-force-nulls($o-color-palette);
|
||||||
|
|
||||||
// Theme colors
|
// Theme colors
|
||||||
$o-theme-color-palette: map-get($o-theme-color-palettes, $o-theme-color-palette-name) or () !default;
|
$o-theme-color-palette: map-get($o-theme-color-palettes, $o-theme-color-palette-name) or () !default;
|
||||||
@@ -42,6 +43,7 @@ $o-theme-color-palette: map-merge((
|
|||||||
'dark': darken(desaturate($-main-color, 80%), min(70%, max(0%, $-main-color-lightness - 10%))), // Does not lower under 10% lightness
|
'dark': darken(desaturate($-main-color, 80%), min(70%, max(0%, $-main-color-lightness - 10%))), // Does not lower under 10% lightness
|
||||||
), o-map-omit($o-theme-color-palette));
|
), o-map-omit($o-theme-color-palette));
|
||||||
$o-theme-color-palette: map-merge($o-base-theme-color-palette, o-map-omit($o-theme-color-palette));
|
$o-theme-color-palette: map-merge($o-base-theme-color-palette, o-map-omit($o-theme-color-palette));
|
||||||
|
$o-theme-color-palette: o-map-force-nulls($o-theme-color-palette);
|
||||||
|
|
||||||
// Gray colors
|
// Gray colors
|
||||||
// Extend grays with transparent ones (for some reason, BS4 create black-50 and
|
// Extend grays with transparent ones (for some reason, BS4 create black-50 and
|
||||||
@@ -49,6 +51,7 @@ $o-theme-color-palette: map-merge($o-base-theme-color-palette, o-map-omit($o-the
|
|||||||
$o-gray-color-palette: map-get($o-gray-color-palettes, $o-gray-color-palette-name) or () !default;
|
$o-gray-color-palette: map-get($o-gray-color-palettes, $o-gray-color-palette-name) or () !default;
|
||||||
$o-gray-color-palette: map-merge($o-transparent-grays, o-map-omit($o-gray-color-palette));
|
$o-gray-color-palette: map-merge($o-transparent-grays, o-map-omit($o-gray-color-palette));
|
||||||
$o-gray-color-palette: map-merge($o-base-gray-color-palette, o-map-omit($o-gray-color-palette));
|
$o-gray-color-palette: map-merge($o-base-gray-color-palette, o-map-omit($o-gray-color-palette));
|
||||||
|
$o-gray-color-palette: o-map-force-nulls($o-gray-color-palette);
|
||||||
|
|
||||||
$o-color-system-initialized: false;
|
$o-color-system-initialized: false;
|
||||||
|
|
||||||
|
|||||||
@@ -787,3 +787,11 @@ section, .oe_img_bg, [data-oe-shape-data] {
|
|||||||
.o_we_bg_filter {
|
.o_we_bg_filter {
|
||||||
@extend %o-we-background-layer;
|
@extend %o-we-background-layer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Gradient
|
||||||
|
// TODO should be in the editor lib since it is handled there... but could not
|
||||||
|
// find the right place for it.
|
||||||
|
.text-gradient {
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
}
|
||||||
|
|||||||
@@ -446,6 +446,21 @@ $o-we-sidebar-content-field-toggle-control-shadow: 0 2px 3px 0 $o-we-bg-darkest
|
|||||||
// Edited content
|
// Edited content
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Useful to force null values (in website, null values set by the user
|
||||||
|
// are removed to act as "removing the user custo and resetting to theme
|
||||||
|
// default": ideally, we should review that system to actually removing the
|
||||||
|
// custos in that case instead of setting a null value but this cannot be
|
||||||
|
// migrated at the moment).
|
||||||
|
@function o-map-force-nulls($map) {
|
||||||
|
$-map: ();
|
||||||
|
@each $key, $value in $map {
|
||||||
|
$-map: map-merge($-map, (
|
||||||
|
$key: if($value == 'NULL', null, $value),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
@return $-map;
|
||||||
|
}
|
||||||
|
|
||||||
$o-support-13-0-color-system: false !default;
|
$o-support-13-0-color-system: false !default;
|
||||||
|
|
||||||
$o-checklist-margin-left: 20px;
|
$o-checklist-margin-left: 20px;
|
||||||
@@ -482,10 +497,6 @@ $o-color-palettes: (
|
|||||||
'o-color-3': #F8F9FA,
|
'o-color-3': #F8F9FA,
|
||||||
'o-color-4': #FFFFFF,
|
'o-color-4': #FFFFFF,
|
||||||
'o-color-5': #343a40,
|
'o-color-5': #343a40,
|
||||||
|
|
||||||
'menu': 2,
|
|
||||||
'footer': 2,
|
|
||||||
'copyright': 5,
|
|
||||||
),
|
),
|
||||||
) !default;
|
) !default;
|
||||||
$o-color-palette-name: 'base-1' !default;
|
$o-color-palette-name: 'base-1' !default;
|
||||||
|
|||||||
@@ -117,8 +117,15 @@ $o-we-zindex: $o-we-overlay-zindex + 1 !default;
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Specific elements
|
// Specific elements
|
||||||
#colorInputButtonGroup label:last-of-type .btn {
|
#colorInputButtonGroup {
|
||||||
margin: 0 1px 0 -1px;
|
label:last-of-type .btn {
|
||||||
|
margin: 0 1px 0 -1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-back-color-preview.dropup .dropdown-menu {
|
||||||
|
// Highlight palette alignment
|
||||||
|
left: -52px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.oe-tablepicker {
|
.oe-tablepicker {
|
||||||
margin: 1px 2px -8px 2px !important;
|
margin: 1px 2px -8px 2px !important;
|
||||||
|
|||||||
@@ -51,12 +51,17 @@
|
|||||||
<span>Theme</span>
|
<span>Theme</span>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" tabindex="2" class="o_we_colorpicker_switch_pane_btn" data-target="custom-colors">
|
<button type="button" tabindex="2" class="o_we_colorpicker_switch_pane_btn" data-target="custom-colors">
|
||||||
<span>Custom</span>
|
<span>Solid</span>
|
||||||
</button>
|
</button>
|
||||||
<button t-if="widget.resetButton" type="button" class="my-1 ml-auto o_we_color_btn o_colorpicker_reset">
|
<button type="button" tabindex="3" class="o_we_colorpicker_switch_pane_btn" data-target="gradients">
|
||||||
<t t-if="widget.withCombinations">None</t>
|
<span>Gradient</span>
|
||||||
<t t-else="">Reset</t>
|
|
||||||
</button>
|
</button>
|
||||||
|
<t t-if="widget.resetButton">
|
||||||
|
<t t-set="trash_title"><t t-if="widget.withCombinations">None</t><t t-else="">Reset</t></t>
|
||||||
|
<button type="button" class="my-1 ml-auto o_we_color_btn o_colorpicker_reset" t-att-title="trash_title">
|
||||||
|
<i class="fa fa-trash"/>
|
||||||
|
</button>
|
||||||
|
</t>
|
||||||
</div>
|
</div>
|
||||||
<div class="o_colorpicker_sections pt-2 px-2 pb-3 bg-black-25" data-color-tab="color-combinations">
|
<div class="o_colorpicker_sections pt-2 px-2 pb-3 bg-black-25" data-color-tab="color-combinations">
|
||||||
<!-- List all Presets -->
|
<!-- List all Presets -->
|
||||||
@@ -68,6 +73,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="o_colorpicker_sections py-3 px-2 bg-black-25" data-color-tab="theme-colors"/>
|
<div class="o_colorpicker_sections py-3 px-2 bg-black-25" data-color-tab="theme-colors"/>
|
||||||
<div class="o_colorpicker_sections py-3 px-2 bg-black-25" data-color-tab="custom-colors"/>
|
<div class="o_colorpicker_sections py-3 px-2 bg-black-25" data-color-tab="custom-colors"/>
|
||||||
|
<div class="o_colorpicker_sections py-3 px-2 bg-black-25" data-color-tab="gradients"/>
|
||||||
</div>
|
</div>
|
||||||
<t t-name="web_editor.colorpalette.combination.btn">
|
<t t-name="web_editor.colorpalette.combination.btn">
|
||||||
<button type="button" class="o_we_color_btn o_we_color_combination_btn"
|
<button type="button" class="o_we_color_btn o_we_color_combination_btn"
|
||||||
|
|||||||
@@ -297,7 +297,7 @@ QUnit.module('web_editor', {}, function () {
|
|||||||
await testUtils.dom.click($('#toolbar .note-back-color-preview .o_we_color_btn.bg-o-color-3'));
|
await testUtils.dom.click($('#toolbar .note-back-color-preview .o_we_color_btn.bg-o-color-3'));
|
||||||
|
|
||||||
assert.strictEqual($field.find('.note-editable').html(),
|
assert.strictEqual($field.find('.note-editable').html(),
|
||||||
'<p>t<font style="background-color: rgb(0, 255, 255);">oto t</font><font style="" class=" bg-o-color-3">oto to</font>to</p><p>tata</p>',
|
'<p>t<font style="background-color: rgb(0, 255, 255);">oto t</font><font style="" class="bg-o-color-3">oto to</font>to</p><p>tata</p>',
|
||||||
"should have rendered the field correctly in edit");
|
"should have rendered the field correctly in edit");
|
||||||
|
|
||||||
form.destroy();
|
form.destroy();
|
||||||
|
|||||||
@@ -60,6 +60,21 @@
|
|||||||
<button data-color="white-75"></button>
|
<button data-color="white-75"></button>
|
||||||
<button data-color="white-85"></button>
|
<button data-color="white-85"></button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="o_colorpicker_section" data-name="predefined_gradients">
|
||||||
|
<t t-set="gradients" t-value="[
|
||||||
|
'linear-gradient(135deg, rgb(255, 204, 51) 0%, rgb(226, 51, 255) 100%)',
|
||||||
|
'linear-gradient(135deg, rgb(102, 153, 255) 0%, rgb(255, 51, 102) 100%)',
|
||||||
|
'linear-gradient(135deg, rgb(47, 128, 237) 0%, rgb(178, 255, 218) 100%)',
|
||||||
|
'linear-gradient(135deg, rgb(203, 94, 238) 0%, rgb(75, 225, 236) 100%)',
|
||||||
|
'linear-gradient(135deg, rgb(214, 255, 127) 0%, rgb(0, 179, 204) 100%)',
|
||||||
|
'linear-gradient(135deg, rgb(255, 222, 69) 0%, rgb(69, 33, 0) 100%)',
|
||||||
|
'linear-gradient(135deg, rgb(222, 222, 222) 0%, rgb(69, 69, 69) 100%)',
|
||||||
|
'linear-gradient(135deg, rgb(255, 222, 202) 0%, rgb(202, 115, 69) 100%)'
|
||||||
|
]"/>
|
||||||
|
<t t-foreach="gradients" t-as="gradient">
|
||||||
|
<button class="w-50" t-attf-style="background-image: #{gradient};" t-att-data-color="gradient"/>
|
||||||
|
</t>
|
||||||
|
</div>
|
||||||
</colorpicker>
|
</colorpicker>
|
||||||
</template>
|
</template>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|||||||
@@ -123,10 +123,12 @@
|
|||||||
<template id="snippet_options_background_color_widget">
|
<template id="snippet_options_background_color_widget">
|
||||||
<we-colorpicker title="Color"
|
<we-colorpicker title="Color"
|
||||||
data-name="bg_color_opt"
|
data-name="bg_color_opt"
|
||||||
|
t-att-data-select-color-combination="'' if with_color_combinations else None"
|
||||||
data-select-style=""
|
data-select-style=""
|
||||||
data-css-property="background-color"
|
data-css-property="background-color"
|
||||||
data-color-prefix="bg-"
|
data-color-prefix="bg-"
|
||||||
t-att-data-with-combinations="with_color_combinations and 'true' or None"
|
t-att-data-with-combinations="with_color_combinations and 'selectColorCombination' or None"
|
||||||
|
t-att-data-with-gradients="with_gradients and 'true' or None"
|
||||||
t-att-data-css-compatible="css_compatible and 'true' or None"/>
|
t-att-data-css-compatible="css_compatible and 'true' or None"/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -145,13 +147,6 @@
|
|||||||
data-no-preview="true">
|
data-no-preview="true">
|
||||||
<i class="fa fa-fw fa-camera"/>
|
<i class="fa fa-fw fa-camera"/>
|
||||||
</we-button>
|
</we-button>
|
||||||
<we-button title="Filter"
|
|
||||||
data-name="bg_filter_toggle_opt"
|
|
||||||
data-dependencies="bg_image_toggle_opt"
|
|
||||||
data-toggle-bg-filter="true"
|
|
||||||
data-no-preview="true">
|
|
||||||
<i class="fa fa-fw fa-paint-brush"/>
|
|
||||||
</we-button>
|
|
||||||
<we-button title="Shape"
|
<we-button title="Shape"
|
||||||
data-toggle-bg-shape="true"
|
data-toggle-bg-shape="true"
|
||||||
t-att-data-dependencies="images_dependencies"
|
t-att-data-dependencies="images_dependencies"
|
||||||
@@ -210,15 +205,22 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Color filter -->
|
<!-- Color filter -->
|
||||||
<div data-option-name="colorFilter"
|
<div t-att-data-js="with_colors and with_color_combinations and 'ColoredLevelBackground' or 'BackgroundToggler'"
|
||||||
t-att-data-selector="selector"
|
t-att-data-selector="selector"
|
||||||
t-att-data-exclude="exclude"
|
t-att-data-exclude="exclude"
|
||||||
t-att-data-target="target">
|
t-att-data-target="target">
|
||||||
<we-colorpicker string="⌙ Color filter"
|
<t t-set="color_filter_dependencies" t-valuef="bg_image_toggle_opt"/>
|
||||||
data-select-style="true"
|
<!--   -->
|
||||||
data-css-property="background-color"
|
<we-colorpicker string=" ⌙ Color filter"
|
||||||
|
t-att-data-dependencies="color_filter_dependencies"
|
||||||
|
data-name="bg_filter_opt"
|
||||||
|
data-select-filter-color=""
|
||||||
data-color-prefix="bg-"
|
data-color-prefix="bg-"
|
||||||
data-apply-to=".o_we_bg_filter"/>
|
data-opacity="0.5"
|
||||||
|
data-with-gradients="1"
|
||||||
|
data-selected-tab="gradients"
|
||||||
|
data-excluded="theme, common"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div data-js="BackgroundShape" string="Shape"
|
<div data-js="BackgroundShape" string="Shape"
|
||||||
|
|||||||
@@ -35,6 +35,13 @@ class Assets(models.AbstractModel):
|
|||||||
'warning': 'null',
|
'warning': 'null',
|
||||||
'danger': 'null',
|
'danger': 'null',
|
||||||
})
|
})
|
||||||
|
# Also reset gradients which are in the "website" values palette
|
||||||
|
self.make_scss_customization('/website/static/src/scss/options/user_values.scss', {
|
||||||
|
'menu-gradient': 'null',
|
||||||
|
'header-boxed-gradient': 'null',
|
||||||
|
'footer-gradient': 'null',
|
||||||
|
'copyright-gradient': 'null',
|
||||||
|
})
|
||||||
|
|
||||||
custom_url = self.make_custom_asset_file_url(url, 'web.assets_common')
|
custom_url = self.make_custom_asset_file_url(url, 'web.assets_common')
|
||||||
updatedFileContent = self.get_asset_content(custom_url) or self.get_asset_content(url)
|
updatedFileContent = self.get_asset_content(custom_url) or self.get_asset_content(url)
|
||||||
|
|||||||
@@ -384,6 +384,7 @@ options.Class.include({
|
|||||||
custom_events: _.extend({}, options.Class.prototype.custom_events || {}, {
|
custom_events: _.extend({}, options.Class.prototype.custom_events || {}, {
|
||||||
'google_fonts_custo_request': '_onGoogleFontsCustoRequest',
|
'google_fonts_custo_request': '_onGoogleFontsCustoRequest',
|
||||||
}),
|
}),
|
||||||
|
specialCheckAndReloadMethodsNames: ['customizeWebsiteViews', 'customizeWebsiteVariable', 'customizeWebsiteColor'],
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
// Options
|
// Options
|
||||||
@@ -422,19 +423,27 @@ options.Class.include({
|
|||||||
}
|
}
|
||||||
for (const widget of widgets) {
|
for (const widget of widgets) {
|
||||||
const methodsNames = widget.getMethodsNames();
|
const methodsNames = widget.getMethodsNames();
|
||||||
if (!methodsNames.includes('customizeWebsiteViews')
|
const specialMethodsNames = [];
|
||||||
&& !methodsNames.includes('customizeWebsiteVariable')
|
for (const methodName of methodsNames) {
|
||||||
&& !methodsNames.includes('customizeWebsiteColor')) {
|
if (this.specialCheckAndReloadMethodsNames.includes(methodName)) {
|
||||||
|
specialMethodsNames.push(methodName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!specialMethodsNames.length) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let paramsReload = false;
|
const isDebugAssets = config.isDebug('assets');
|
||||||
if (widget.getMethodsParams('customizeWebsiteViews').reload
|
let paramsReload = isDebugAssets;
|
||||||
|| widget.getMethodsParams('customizeWebsiteVariable').reload
|
if (!isDebugAssets) {
|
||||||
|| widget.getMethodsParams('customizeWebsiteColor').reload) {
|
for (const methodName of specialMethodsNames) {
|
||||||
paramsReload = true;
|
if (widget.getMethodsParams(methodName).reload) {
|
||||||
|
paramsReload = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (paramsReload || config.isDebug('assets')) {
|
if (paramsReload) {
|
||||||
return (config.isDebug('assets') ? _t("It appears you are in debug=assets mode, all theme customization options require a page reload in this mode.") : true);
|
return (isDebugAssets ? _t("It appears you are in debug=assets mode, all theme customization options require a page reload in this mode.") : true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -498,7 +507,7 @@ options.Class.include({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.reload || config.isDebug('assets')) {
|
if (params.reload || config.isDebug('assets') || params.noBundleReload) {
|
||||||
// Caller will reload the page, nothing needs to be done anymore.
|
// Caller will reload the page, nothing needs to be done anymore.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -543,7 +552,7 @@ options.Class.include({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this._makeSCSSCusto(url, finalColors);
|
return this._makeSCSSCusto(url, finalColors, params.nullValue);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
@@ -551,7 +560,7 @@ options.Class.include({
|
|||||||
_customizeWebsiteVariable: async function (value, params) {
|
_customizeWebsiteVariable: async function (value, params) {
|
||||||
return this._makeSCSSCusto('/website/static/src/scss/options/user_values.scss', {
|
return this._makeSCSSCusto('/website/static/src/scss/options/user_values.scss', {
|
||||||
[params.variable]: value,
|
[params.variable]: value,
|
||||||
});
|
}, params.nullValue);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
@@ -582,12 +591,12 @@ options.Class.include({
|
|||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_makeSCSSCusto: async function (url, values) {
|
_makeSCSSCusto: async function (url, values, defaultValue = 'null') {
|
||||||
return this._rpc({
|
return this._rpc({
|
||||||
route: '/website/make_scss_custo',
|
route: '/website/make_scss_custo',
|
||||||
params: {
|
params: {
|
||||||
'url': url,
|
'url': url,
|
||||||
'values': _.mapObject(values, v => v || 'null'),
|
'values': _.mapObject(values, v => v || defaultValue),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -700,6 +709,7 @@ options.registry.BackgroundToggler.include({
|
|||||||
*/
|
*/
|
||||||
toggleBgVideo(previewMode, widgetValue, params) {
|
toggleBgVideo(previewMode, widgetValue, params) {
|
||||||
if (!widgetValue) {
|
if (!widgetValue) {
|
||||||
|
this.$target.find('> .o_we_bg_filter').remove();
|
||||||
// TODO: use setWidgetValue instead of calling background directly when possible
|
// TODO: use setWidgetValue instead of calling background directly when possible
|
||||||
const [bgVideoWidget] = this._requestUserValueWidgets('bg_video_opt');
|
const [bgVideoWidget] = this._requestUserValueWidgets('bg_video_opt');
|
||||||
const bgVideoOpt = bgVideoWidget.getParent();
|
const bgVideoOpt = bgVideoWidget.getParent();
|
||||||
@@ -1946,6 +1956,54 @@ options.registry.collapse = options.Class.extend({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
options.registry.WebsiteLevelColor = options.Class.extend({
|
||||||
|
specialCheckAndReloadMethodsNames: options.Class.prototype.specialCheckAndReloadMethodsNames
|
||||||
|
.concat(['customizeWebsiteLayer2Color']),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see this.selectClass for parameters
|
||||||
|
*/
|
||||||
|
async customizeWebsiteLayer2Color(previewMode, widgetValue, params) {
|
||||||
|
if (previewMode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
params.color = params.layerColor;
|
||||||
|
params.variable = params.layerGradient;
|
||||||
|
let color = undefined;
|
||||||
|
let gradient = undefined;
|
||||||
|
if (weUtils.isColorGradient(widgetValue)) {
|
||||||
|
color = '';
|
||||||
|
gradient = widgetValue;
|
||||||
|
} else {
|
||||||
|
color = widgetValue;
|
||||||
|
gradient = '';
|
||||||
|
}
|
||||||
|
await this.customizeWebsiteVariable(previewMode, gradient, params);
|
||||||
|
params.noBundleReload = false;
|
||||||
|
return this.customizeWebsiteColor(previewMode, color, params);
|
||||||
|
},
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
// Private
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
async _computeWidgetState(methodName, params) {
|
||||||
|
if (methodName === 'customizeWebsiteLayer2Color') {
|
||||||
|
params.variable = params.layerGradient;
|
||||||
|
const gradient = await this._computeWidgetState('customizeWebsiteVariable', params);
|
||||||
|
if (gradient) {
|
||||||
|
return gradient.substring(1, gradient.length - 1); // Unquote
|
||||||
|
}
|
||||||
|
params.color = params.layerColor;
|
||||||
|
return this._computeWidgetState('customizeWebsiteColor', params);
|
||||||
|
}
|
||||||
|
return this._super(...arguments);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
options.registry.HeaderNavbar = options.Class.extend({
|
options.registry.HeaderNavbar = options.Class.extend({
|
||||||
/**
|
/**
|
||||||
* Particular case: we want the option to be associated on the header navbar
|
* Particular case: we want the option to be associated on the header navbar
|
||||||
@@ -2633,10 +2691,23 @@ options.registry.CoverProperties = options.Class.extend({
|
|||||||
name: 'bg_color_opt',
|
name: 'bg_color_opt',
|
||||||
onSuccess: _widget => colorPickerWidget = _widget,
|
onSuccess: _widget => colorPickerWidget = _widget,
|
||||||
});
|
});
|
||||||
const color = colorPickerWidget._value;
|
// TODO there is probably a better way and this should be refactored to
|
||||||
const isCSSColor = ColorpickerWidget.isCSSColor(color);
|
// use more standard colorpicker+imagepicker structure
|
||||||
this.$target[0].dataset.bgColorClass = isCSSColor ? '' : weUtils.computeColorClasses([color])[0];
|
const ccValue = colorPickerWidget._ccValue;
|
||||||
this.$target[0].dataset.bgColorStyle = isCSSColor ? `background-color: ${color};` : '';
|
const colorOrGradient = colorPickerWidget._value;
|
||||||
|
const isGradient = weUtils.isColorGradient(colorOrGradient);
|
||||||
|
const isCSSColor = !isGradient && ColorpickerWidget.isCSSColor(colorOrGradient);
|
||||||
|
const colorNames = [];
|
||||||
|
if (ccValue) {
|
||||||
|
colorNames.push(ccValue);
|
||||||
|
}
|
||||||
|
if (!isGradient && !isCSSColor) {
|
||||||
|
colorNames.push(colorOrGradient);
|
||||||
|
}
|
||||||
|
this.$target[0].dataset.bgColorClass = weUtils.computeColorClasses(colorNames).join(' ');
|
||||||
|
this.$target[0].dataset.bgColorStyle =
|
||||||
|
isCSSColor ? `background-color: ${colorOrGradient};` :
|
||||||
|
isGradient ? `background-color: rgba(0, 0, 0, 0); background-image: ${colorOrGradient};` : '';
|
||||||
},
|
},
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -6,10 +6,14 @@
|
|||||||
$o-base-color-palette: map-merge($o-base-color-palette, (
|
$o-base-color-palette: map-merge($o-base-color-palette, (
|
||||||
'body': $o-portal-default-body-bg,
|
'body': $o-portal-default-body-bg,
|
||||||
'menu': 1, // o_cc1
|
'menu': 1, // o_cc1
|
||||||
|
'menu-custom': null,
|
||||||
'menu-border-color': null, // Default to classes used on the template
|
'menu-border-color': null, // Default to classes used on the template
|
||||||
'header-boxed': '200',
|
'header-boxed': null,
|
||||||
|
'header-boxed-custom': '200',
|
||||||
'footer': 5, // o_cc5
|
'footer': 5, // o_cc5
|
||||||
'copyright': 'black-15',
|
'footer-custom': null,
|
||||||
|
'copyright': null,
|
||||||
|
'copyright-custom': null,
|
||||||
));
|
));
|
||||||
|
|
||||||
// By default, all user color palette values are null. Each null value is
|
// By default, all user color palette values are null. Each null value is
|
||||||
@@ -42,6 +46,14 @@ $o-theme-figcaption-opacity: 0.6;
|
|||||||
|
|
||||||
$o-color-palettes: map-merge($o-color-palettes,
|
$o-color-palettes: map-merge($o-color-palettes,
|
||||||
(
|
(
|
||||||
|
'base-1': map-merge(map-get($o-color-palettes, 'base-1'), (
|
||||||
|
'copyright-custom': 'black-15',
|
||||||
|
)),
|
||||||
|
'base-2': map-merge(map-get($o-color-palettes, 'base-2'), (
|
||||||
|
'menu': 2,
|
||||||
|
'footer': 2,
|
||||||
|
'copyright': 5,
|
||||||
|
)),
|
||||||
'generic-1': (
|
'generic-1': (
|
||||||
'o-color-1': #984c46,
|
'o-color-1': #984c46,
|
||||||
'o-color-2': #23323b,
|
'o-color-2': #23323b,
|
||||||
@@ -51,6 +63,7 @@ $o-color-palettes: map-merge($o-color-palettes,
|
|||||||
|
|
||||||
'menu': 3,
|
'menu': 3,
|
||||||
'footer': 3,
|
'footer': 3,
|
||||||
|
'copyright-custom': 'black-15',
|
||||||
),
|
),
|
||||||
'generic-2': (
|
'generic-2': (
|
||||||
'o-color-1': #B99932,
|
'o-color-1': #B99932,
|
||||||
@@ -68,6 +81,8 @@ $o-color-palettes: map-merge($o-color-palettes,
|
|||||||
'o-color-3': #fdf8ef,
|
'o-color-3': #fdf8ef,
|
||||||
'o-color-4': #FFFFFF,
|
'o-color-4': #FFFFFF,
|
||||||
'o-color-5': #212c39,
|
'o-color-5': #212c39,
|
||||||
|
|
||||||
|
'copyright-custom': 'black-15',
|
||||||
),
|
),
|
||||||
'generic-4': (
|
'generic-4': (
|
||||||
'o-color-1': #6E7993,
|
'o-color-1': #6E7993,
|
||||||
@@ -77,6 +92,7 @@ $o-color-palettes: map-merge($o-color-palettes,
|
|||||||
'o-color-5': #313347,
|
'o-color-5': #313347,
|
||||||
|
|
||||||
'menu': 5,
|
'menu': 5,
|
||||||
|
'copyright-custom': 'black-15',
|
||||||
),
|
),
|
||||||
'generic-5': (
|
'generic-5': (
|
||||||
'o-color-1': #F7CF41,
|
'o-color-1': #F7CF41,
|
||||||
@@ -87,6 +103,7 @@ $o-color-palettes: map-merge($o-color-palettes,
|
|||||||
|
|
||||||
'menu': 3,
|
'menu': 3,
|
||||||
'footer': 3,
|
'footer': 3,
|
||||||
|
'copyright-custom': 'black-15',
|
||||||
),
|
),
|
||||||
'generic-6': (
|
'generic-6': (
|
||||||
'o-color-1': #45859A,
|
'o-color-1': #45859A,
|
||||||
@@ -139,6 +156,7 @@ $o-color-palettes: map-merge($o-color-palettes,
|
|||||||
'o-color-5': #000000,
|
'o-color-5': #000000,
|
||||||
|
|
||||||
'menu': 5,
|
'menu': 5,
|
||||||
|
'copyright-custom': 'black-15',
|
||||||
),
|
),
|
||||||
'generic-10': (
|
'generic-10': (
|
||||||
'o-color-1': #b56355,
|
'o-color-1': #b56355,
|
||||||
@@ -148,6 +166,7 @@ $o-color-palettes: map-merge($o-color-palettes,
|
|||||||
'o-color-5': #343733,
|
'o-color-5': #343733,
|
||||||
|
|
||||||
'footer': 2,
|
'footer': 2,
|
||||||
|
'copyright-custom': 'black-15',
|
||||||
),
|
),
|
||||||
'generic-11': (
|
'generic-11': (
|
||||||
'o-color-1': #01ACAB,
|
'o-color-1': #01ACAB,
|
||||||
@@ -157,6 +176,7 @@ $o-color-palettes: map-merge($o-color-palettes,
|
|||||||
'o-color-5': #000000,
|
'o-color-5': #000000,
|
||||||
|
|
||||||
'footer': 1,
|
'footer': 1,
|
||||||
|
'copyright-custom': 'black-15',
|
||||||
),
|
),
|
||||||
'generic-12': (
|
'generic-12': (
|
||||||
'o-color-1': #926190,
|
'o-color-1': #926190,
|
||||||
@@ -172,6 +192,8 @@ $o-color-palettes: map-merge($o-color-palettes,
|
|||||||
'o-cc5-headings': 'o-color-4',
|
'o-cc5-headings': 'o-color-4',
|
||||||
'o-cc5-link': 'o-color-4',
|
'o-cc5-link': 'o-color-4',
|
||||||
'o-cc5-text': rgba(#fff, .6),
|
'o-cc5-text': rgba(#fff, .6),
|
||||||
|
|
||||||
|
'copyright-custom': 'black-15',
|
||||||
),
|
),
|
||||||
'generic-13': (
|
'generic-13': (
|
||||||
'o-color-1': #478FA2,
|
'o-color-1': #478FA2,
|
||||||
@@ -240,6 +262,7 @@ $o-color-palettes: map-merge($o-color-palettes,
|
|||||||
|
|
||||||
'menu': 5,
|
'menu': 5,
|
||||||
'footer': 5,
|
'footer': 5,
|
||||||
|
'copyright-custom': 'black-15',
|
||||||
),
|
),
|
||||||
'anelusia-1': (
|
'anelusia-1': (
|
||||||
'o-color-1': #000000,
|
'o-color-1': #000000,
|
||||||
@@ -284,6 +307,8 @@ $o-color-palettes: map-merge($o-color-palettes,
|
|||||||
|
|
||||||
'o-cc5-link': #218bdf,
|
'o-cc5-link': #218bdf,
|
||||||
'o-cc5-btn-primary': #218bdf,
|
'o-cc5-btn-primary': #218bdf,
|
||||||
|
|
||||||
|
'copyright-custom': 'black-15',
|
||||||
),
|
),
|
||||||
'anelusia-4': (
|
'anelusia-4': (
|
||||||
'o-color-1': #dc143c,
|
'o-color-1': #dc143c,
|
||||||
@@ -291,6 +316,8 @@ $o-color-palettes: map-merge($o-color-palettes,
|
|||||||
'o-color-3': #faf7e1,
|
'o-color-3': #faf7e1,
|
||||||
'o-color-4': #ffffff,
|
'o-color-4': #ffffff,
|
||||||
'o-color-5': #222222,
|
'o-color-5': #222222,
|
||||||
|
|
||||||
|
'copyright-custom': 'black-15',
|
||||||
),
|
),
|
||||||
'anelusia-5': (
|
'anelusia-5': (
|
||||||
'o-color-1': #32c0c9,
|
'o-color-1': #32c0c9,
|
||||||
@@ -300,6 +327,7 @@ $o-color-palettes: map-merge($o-color-palettes,
|
|||||||
'o-color-5': #2e2e2e,
|
'o-color-5': #2e2e2e,
|
||||||
|
|
||||||
'footer': 1,
|
'footer': 1,
|
||||||
|
'copyright-custom': 'black-15',
|
||||||
),
|
),
|
||||||
'anelusia-6': (
|
'anelusia-6': (
|
||||||
'o-color-1': #e27b92,
|
'o-color-1': #e27b92,
|
||||||
@@ -313,6 +341,7 @@ $o-color-palettes: map-merge($o-color-palettes,
|
|||||||
|
|
||||||
'menu': 2,
|
'menu': 2,
|
||||||
'footer': 2,
|
'footer': 2,
|
||||||
|
'copyright-custom': 'black-15',
|
||||||
),
|
),
|
||||||
'artists-1': (
|
'artists-1': (
|
||||||
'o-color-1': #ab44ff,
|
'o-color-1': #ab44ff,
|
||||||
@@ -324,6 +353,7 @@ $o-color-palettes: map-merge($o-color-palettes,
|
|||||||
'o-cc5-text': 'o-color-3',
|
'o-cc5-text': 'o-color-3',
|
||||||
|
|
||||||
'menu': 5,
|
'menu': 5,
|
||||||
|
'copyright-custom': 'black-15',
|
||||||
),
|
),
|
||||||
'artists-2': (
|
'artists-2': (
|
||||||
'o-color-1': #1ad68f,
|
'o-color-1': #1ad68f,
|
||||||
@@ -333,6 +363,7 @@ $o-color-palettes: map-merge($o-color-palettes,
|
|||||||
'o-color-5': #000000,
|
'o-color-5': #000000,
|
||||||
|
|
||||||
'menu': 2,
|
'menu': 2,
|
||||||
|
'copyright-custom': 'black-15',
|
||||||
),
|
),
|
||||||
'artists-3': (
|
'artists-3': (
|
||||||
'o-color-1': #007882,
|
'o-color-1': #007882,
|
||||||
@@ -355,6 +386,7 @@ $o-color-palettes: map-merge($o-color-palettes,
|
|||||||
|
|
||||||
'menu': 2,
|
'menu': 2,
|
||||||
'footer': 2,
|
'footer': 2,
|
||||||
|
'copyright-custom': 'black-15',
|
||||||
),
|
),
|
||||||
'artists-5': (
|
'artists-5': (
|
||||||
'o-color-1': #f48847,
|
'o-color-1': #f48847,
|
||||||
@@ -364,6 +396,7 @@ $o-color-palettes: map-merge($o-color-palettes,
|
|||||||
'o-color-5': #000000,
|
'o-color-5': #000000,
|
||||||
|
|
||||||
'footer': 3,
|
'footer': 3,
|
||||||
|
'copyright-custom': 'black-15',
|
||||||
),
|
),
|
||||||
'artists-6': (
|
'artists-6': (
|
||||||
'o-color-1': #95d2e5,
|
'o-color-1': #95d2e5,
|
||||||
@@ -374,6 +407,7 @@ $o-color-palettes: map-merge($o-color-palettes,
|
|||||||
|
|
||||||
'menu': 2,
|
'menu': 2,
|
||||||
'footer': 2,
|
'footer': 2,
|
||||||
|
'copyright-custom': 'black-15',
|
||||||
),
|
),
|
||||||
'avantgarde-1': (
|
'avantgarde-1': (
|
||||||
'o-color-1': #ee4980,
|
'o-color-1': #ee4980,
|
||||||
@@ -403,6 +437,7 @@ $o-color-palettes: map-merge($o-color-palettes,
|
|||||||
|
|
||||||
'menu': 4,
|
'menu': 4,
|
||||||
'footer': 4,
|
'footer': 4,
|
||||||
|
'copyright-custom': 'black-15',
|
||||||
),
|
),
|
||||||
'avantgarde-3': (
|
'avantgarde-3': (
|
||||||
'o-color-1': #f08e80,
|
'o-color-1': #f08e80,
|
||||||
@@ -465,6 +500,8 @@ $o-color-palettes: map-merge($o-color-palettes,
|
|||||||
'o-cc2-headings': 'o-color-2',
|
'o-cc2-headings': 'o-color-2',
|
||||||
'o-cc3-headings': 'o-color-3',
|
'o-cc3-headings': 'o-color-3',
|
||||||
'o-cc5-headings': 'o-color-1',
|
'o-cc5-headings': 'o-color-1',
|
||||||
|
|
||||||
|
'copyright-custom': 'black-15',
|
||||||
),
|
),
|
||||||
'avantgarde-7': (
|
'avantgarde-7': (
|
||||||
'o-color-1': #f48847,
|
'o-color-1': #f48847,
|
||||||
@@ -475,6 +512,8 @@ $o-color-palettes: map-merge($o-color-palettes,
|
|||||||
|
|
||||||
'o-cc2-headings': 'o-color-1',
|
'o-cc2-headings': 'o-color-1',
|
||||||
'o-cc5-headings': 'o-color-1',
|
'o-cc5-headings': 'o-color-1',
|
||||||
|
|
||||||
|
'copyright-custom': 'black-15',
|
||||||
),
|
),
|
||||||
'avantgarde-8': (
|
'avantgarde-8': (
|
||||||
'o-color-1': #278f84,
|
'o-color-1': #278f84,
|
||||||
@@ -485,6 +524,8 @@ $o-color-palettes: map-merge($o-color-palettes,
|
|||||||
|
|
||||||
'o-cc2-headings': 'o-color-1',
|
'o-cc2-headings': 'o-color-1',
|
||||||
'o-cc5-headings': 'o-color-1',
|
'o-cc5-headings': 'o-color-1',
|
||||||
|
|
||||||
|
'copyright-custom': 'black-15',
|
||||||
),
|
),
|
||||||
'beauty-1': (
|
'beauty-1': (
|
||||||
'o-color-1': #df699c,
|
'o-color-1': #df699c,
|
||||||
@@ -553,6 +594,8 @@ $o-color-palettes: map-merge($o-color-palettes,
|
|||||||
'o-color-5': #112625,
|
'o-color-5': #112625,
|
||||||
|
|
||||||
'o-cc2-link': 'o-color-5',
|
'o-cc2-link': 'o-color-5',
|
||||||
|
|
||||||
|
'copyright-custom': 'black-15',
|
||||||
),
|
),
|
||||||
'bewise-1': (
|
'bewise-1': (
|
||||||
'o-color-1': #162238,
|
'o-color-1': #162238,
|
||||||
@@ -861,7 +904,7 @@ $o-color-palettes: map-merge($o-color-palettes,
|
|||||||
|
|
||||||
'header': 1,
|
'header': 1,
|
||||||
'footer': 3,
|
'footer': 3,
|
||||||
'copyright': 'black-15',
|
'copyright-custom': 'black-15',
|
||||||
),
|
),
|
||||||
'enark-1': (
|
'enark-1': (
|
||||||
'o-color-1': #7b97af,
|
'o-color-1': #7b97af,
|
||||||
@@ -873,6 +916,8 @@ $o-color-palettes: map-merge($o-color-palettes,
|
|||||||
'o-cc1-text': 'o-color-2',
|
'o-cc1-text': 'o-color-2',
|
||||||
'o-cc1-btn-primary': 'o-color-2',
|
'o-cc1-btn-primary': 'o-color-2',
|
||||||
'o-cc5-link': 'o-color-4',
|
'o-cc5-link': 'o-color-4',
|
||||||
|
|
||||||
|
'copyright-custom': 'black-15',
|
||||||
),
|
),
|
||||||
'enark-2': (
|
'enark-2': (
|
||||||
'o-color-1': #41cb7a,
|
'o-color-1': #41cb7a,
|
||||||
@@ -936,7 +981,7 @@ $o-color-palettes: map-merge($o-color-palettes,
|
|||||||
'body': 'black-15',
|
'body': 'black-15',
|
||||||
'menu': 1,
|
'menu': 1,
|
||||||
'footer': 5,
|
'footer': 5,
|
||||||
'copyright': 'black-25',
|
'copyright-custom': 'black-25',
|
||||||
),
|
),
|
||||||
'graphene-2': (
|
'graphene-2': (
|
||||||
'o-color-1': #c8a47e,
|
'o-color-1': #c8a47e,
|
||||||
@@ -950,6 +995,8 @@ $o-color-palettes: map-merge($o-color-palettes,
|
|||||||
'o-cc3-headings': 'o-color-4',
|
'o-cc3-headings': 'o-color-4',
|
||||||
'o-cc4-headings': 'o-color-5',
|
'o-cc4-headings': 'o-color-5',
|
||||||
'o-cc5-text': #b2b2b2,
|
'o-cc5-text': #b2b2b2,
|
||||||
|
|
||||||
|
'copyright-custom': 'black-15',
|
||||||
),
|
),
|
||||||
'graphene-3': (
|
'graphene-3': (
|
||||||
'o-color-1': #39588e,
|
'o-color-1': #39588e,
|
||||||
@@ -962,6 +1009,7 @@ $o-color-palettes: map-merge($o-color-palettes,
|
|||||||
|
|
||||||
'menu': 2,
|
'menu': 2,
|
||||||
'footer': 2,
|
'footer': 2,
|
||||||
|
'copyright-custom': 'black-15',
|
||||||
),
|
),
|
||||||
'graphene-4': (
|
'graphene-4': (
|
||||||
'o-color-1': #f48747,
|
'o-color-1': #f48747,
|
||||||
@@ -971,6 +1019,7 @@ $o-color-palettes: map-merge($o-color-palettes,
|
|||||||
'o-color-5': #282d30,
|
'o-color-5': #282d30,
|
||||||
|
|
||||||
'menu': 5,
|
'menu': 5,
|
||||||
|
'copyright-custom': 'black-15',
|
||||||
),
|
),
|
||||||
'kea-1': (
|
'kea-1': (
|
||||||
'o-color-1': #e991a3,
|
'o-color-1': #e991a3,
|
||||||
@@ -1137,6 +1186,8 @@ $o-color-palettes: map-merge($o-color-palettes,
|
|||||||
'o-color-3': #f5f5f5,
|
'o-color-3': #f5f5f5,
|
||||||
'o-color-4': #ffffff,
|
'o-color-4': #ffffff,
|
||||||
'o-color-5': #111111,
|
'o-color-5': #111111,
|
||||||
|
|
||||||
|
'copyright-custom': 'black-15',
|
||||||
),
|
),
|
||||||
'monglia-2': (
|
'monglia-2': (
|
||||||
'o-color-1': #8f8747,
|
'o-color-1': #8f8747,
|
||||||
@@ -1144,6 +1195,8 @@ $o-color-palettes: map-merge($o-color-palettes,
|
|||||||
'o-color-3': #f0ecdb,
|
'o-color-3': #f0ecdb,
|
||||||
'o-color-4': #ffffff,
|
'o-color-4': #ffffff,
|
||||||
'o-color-5': #21263a,
|
'o-color-5': #21263a,
|
||||||
|
|
||||||
|
'copyright-custom': 'black-15',
|
||||||
),
|
),
|
||||||
'monglia-3': (
|
'monglia-3': (
|
||||||
'o-color-1': #c0ce75,
|
'o-color-1': #c0ce75,
|
||||||
@@ -1151,6 +1204,8 @@ $o-color-palettes: map-merge($o-color-palettes,
|
|||||||
'o-color-3': #dedede,
|
'o-color-3': #dedede,
|
||||||
'o-color-4': #ffffff,
|
'o-color-4': #ffffff,
|
||||||
'o-color-5': #3e3e3e,
|
'o-color-5': #3e3e3e,
|
||||||
|
|
||||||
|
'copyright-custom': 'black-15',
|
||||||
),
|
),
|
||||||
'monglia-4': (
|
'monglia-4': (
|
||||||
'o-color-1': #1ad68f,
|
'o-color-1': #1ad68f,
|
||||||
@@ -1158,6 +1213,8 @@ $o-color-palettes: map-merge($o-color-palettes,
|
|||||||
'o-color-3': #cfcfcf,
|
'o-color-3': #cfcfcf,
|
||||||
'o-color-4': #ffffff,
|
'o-color-4': #ffffff,
|
||||||
'o-color-5': #000000,
|
'o-color-5': #000000,
|
||||||
|
|
||||||
|
'copyright-custom': 'black-15',
|
||||||
),
|
),
|
||||||
'monglia-5': (
|
'monglia-5': (
|
||||||
'o-color-1': #c5ca60,
|
'o-color-1': #c5ca60,
|
||||||
@@ -1506,6 +1563,7 @@ $o-color-palettes: map-merge($o-color-palettes,
|
|||||||
'o-cc5-link': #70c7cf,
|
'o-cc5-link': #70c7cf,
|
||||||
|
|
||||||
'menu': 5,
|
'menu': 5,
|
||||||
|
'copyright-custom': 'black-15',
|
||||||
),
|
),
|
||||||
'treehouse-1': (
|
'treehouse-1': (
|
||||||
'o-color-1': #68b581,
|
'o-color-1': #68b581,
|
||||||
@@ -1868,6 +1926,12 @@ $o-base-website-values-palette: (
|
|||||||
'navbar-font': null,
|
'navbar-font': null,
|
||||||
'buttons-font': null,
|
'buttons-font': null,
|
||||||
|
|
||||||
|
// Gradients
|
||||||
|
'menu-gradient': null,
|
||||||
|
'header-boxed-gradient': null,
|
||||||
|
'footer-gradient': null,
|
||||||
|
'copyright-gradient': null,
|
||||||
|
|
||||||
'header-template': 'default', // 'default' / 'hamburger' / 'vertical' / 'sidebar'
|
'header-template': 'default', // 'default' / 'hamburger' / 'vertical' / 'sidebar'
|
||||||
'header-font-size': null, // Default to BS (normal font-size)
|
'header-font-size': null, // Default to BS (normal font-size)
|
||||||
'header-links-style': 'default', // 'default' / 'fill' / 'outline' / 'pills' / 'block' / 'border-bottom'
|
'header-links-style': 'default', // 'default' / 'fill' / 'outline' / 'pills' / 'block' / 'border-bottom'
|
||||||
@@ -1985,3 +2049,11 @@ $o-theme-font-configs: (
|
|||||||
@include o-position-absolute($top: 0, $left: 0);
|
@include o-position-absolute($top: 0, $left: 0);
|
||||||
padding: 0.25rem 1rem;
|
padding: 0.25rem 1rem;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@mixin o-add-gradient($key) {
|
||||||
|
$-gradient: o-website-value($key);
|
||||||
|
@if $-gradient {
|
||||||
|
background-color: rgba(0, 0, 0, 0);
|
||||||
|
background-image: $-gradient;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ $o-website-values-palettes: append($o-website-values-palettes, $-actual-user-web
|
|||||||
|
|
||||||
// Enable last website values palette, which is now the user customized one
|
// Enable last website values palette, which is now the user customized one
|
||||||
$o-website-values-palette-number: length($o-website-values-palettes);
|
$o-website-values-palette-number: length($o-website-values-palettes);
|
||||||
$o-website-values: $-actual-user-website-values-palette !default;
|
$o-website-values: o-map-force-nulls($-actual-user-website-values-palette) !default;
|
||||||
@function o-website-value($key) {
|
@function o-website-value($key) {
|
||||||
@return map-get($o-website-values, $key);
|
@return map-get($o-website-values, $key);
|
||||||
}
|
}
|
||||||
@@ -134,6 +134,27 @@ $o-has-customized-colors:
|
|||||||
// palette.
|
// palette.
|
||||||
$-palette-default: map-get($o-color-palettes, $o-color-palette-name) or ();
|
$-palette-default: map-get($o-color-palettes, $o-color-palette-name) or ();
|
||||||
$-actual-user-color-palette: map-merge($-palette-default, o-map-omit($o-user-color-palette));
|
$-actual-user-color-palette: map-merge($-palette-default, o-map-omit($o-user-color-palette));
|
||||||
|
// Compatibility with old values in old names
|
||||||
|
@each $name, $custom-name in (
|
||||||
|
// Each of those values were either a number for a color combination, a
|
||||||
|
// string for a color name or a color. Now they should only be a number for
|
||||||
|
// a color combination and the other value types for the color name/value
|
||||||
|
// are handled by another variable. That variable can also contain a string
|
||||||
|
// for a color gradient.
|
||||||
|
'menu': 'menu-custom',
|
||||||
|
'header-boxed': 'header-boxed-custom',
|
||||||
|
'footer': 'footer-custom',
|
||||||
|
'copyright': 'copyright-custom'
|
||||||
|
) {
|
||||||
|
$-base-value: map-get($-actual-user-color-palette, $name);
|
||||||
|
@if $-base-value and $-base-value != 'NULL' and type-of($-base-value) != 'number' {
|
||||||
|
$-base-custom-value: map-get($-actual-user-color-palette, $custom-name);
|
||||||
|
$-actual-user-color-palette: map-merge($-actual-user-color-palette, (
|
||||||
|
$name: 1,
|
||||||
|
$custom-name: $-base-custom-value or $-base-value,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
$o-color-palettes: map-merge($o-color-palettes, ('user-palette': $-actual-user-color-palette));
|
$o-color-palettes: map-merge($o-color-palettes, ('user-palette': $-actual-user-color-palette));
|
||||||
|
|
||||||
// Gray palette
|
// Gray palette
|
||||||
@@ -174,6 +195,7 @@ $-actual-user-theme-color-palette: map-remove($-actual-user-theme-color-palette,
|
|||||||
'secondary'
|
'secondary'
|
||||||
);
|
);
|
||||||
$o-theme-color-palettes: map-merge($o-theme-color-palettes, ('user-palette': $-actual-user-theme-color-palette));
|
$o-theme-color-palettes: map-merge($o-theme-color-palettes, ('user-palette': $-actual-user-theme-color-palette));
|
||||||
|
|
||||||
// ---
|
// ---
|
||||||
|
|
||||||
// Enable last color and theme color palettes, which are now the user customized
|
// Enable last color and theme color palettes, which are now the user customized
|
||||||
|
|||||||
@@ -136,7 +136,8 @@ $-seen-urls: ();
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Vertical alignment when top-menu has visually "no background"
|
// Vertical alignment when top-menu has visually "no background"
|
||||||
@if o-color('menu') == o-color('body') {
|
$-menu-color: o-color('menu-custom') or o-color('menu');
|
||||||
|
@if (not $-menu-color or $-menu-color == o-color('body')) {
|
||||||
> header {
|
> header {
|
||||||
.navbar, .container {
|
.navbar, .container {
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
@@ -226,9 +227,12 @@ $-seen-urls: ();
|
|||||||
// Note: this only works because navbar-light is defined before navbar-dark,
|
// Note: this only works because navbar-light is defined before navbar-dark,
|
||||||
// we may want to use a safest way when possible.
|
// we may want to use a safest way when possible.
|
||||||
@include o-apply-colors('menu');
|
@include o-apply-colors('menu');
|
||||||
@if (color-yiq(o-color('menu')) != $yiq-text-dark) {
|
@include o-apply-colors('menu-custom');
|
||||||
|
$-menu-color: o-color('menu-custom') or o-color('menu');
|
||||||
|
@if ($-menu-color and color-yiq($-menu-color) != $yiq-text-dark) {
|
||||||
@extend .navbar-dark;
|
@extend .navbar-dark;
|
||||||
}
|
}
|
||||||
|
@include o-add-gradient('menu-gradient');
|
||||||
}
|
}
|
||||||
|
|
||||||
$-header-nav-link-height: $nav-link-height;
|
$-header-nav-link-height: $nav-link-height;
|
||||||
@@ -258,9 +262,14 @@ $-header-nav-link-height: $nav-link-height;
|
|||||||
|
|
||||||
.o_footer {
|
.o_footer {
|
||||||
@include o-apply-colors('footer');
|
@include o-apply-colors('footer');
|
||||||
|
@include o-apply-colors('footer-custom');
|
||||||
|
@include o-add-gradient('footer-gradient');
|
||||||
|
|
||||||
.o_footer_copyright {
|
.o_footer_copyright {
|
||||||
@include o-apply-colors('copyright', $background: o-color('footer'));
|
$-footer-color: o-color('footer-custom') or o-color('footer');
|
||||||
|
@include o-apply-colors('copyright', $background: $-footer-color);
|
||||||
|
@include o-apply-colors('copyright-custom', $background: $-footer-color);
|
||||||
|
@include o-add-gradient('copyright-gradient');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -286,7 +295,7 @@ h6 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Texts
|
// Texts
|
||||||
font[style*='background'],
|
font[style*='background']:not(.text-gradient),
|
||||||
font[class*='bg-'] {
|
font[class*='bg-'] {
|
||||||
padding: 2px 6px 4px;
|
padding: 2px 6px 4px;
|
||||||
}
|
}
|
||||||
@@ -946,7 +955,8 @@ header {
|
|||||||
}
|
}
|
||||||
min-width: 250px;
|
min-width: 250px;
|
||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
background-color: o-color('menu');
|
background-color: o-color('menu-custom') or o-color('menu');
|
||||||
|
@include o-add-gradient('menu-gradient');
|
||||||
transition: transform $-transition-duration cubic-bezier(.694, .0482, .335, 1);
|
transition: transform $-transition-duration cubic-bezier(.694, .0482, .335, 1);
|
||||||
|
|
||||||
@if $-hamburger-center {
|
@if $-hamburger-center {
|
||||||
@@ -1102,6 +1112,8 @@ header {
|
|||||||
} @else if o-website-value('header-template') == 'boxed' {
|
} @else if o-website-value('header-template') == 'boxed' {
|
||||||
#wrapwrap:not(.o_header_overlay) .o_header_boxed_background {
|
#wrapwrap:not(.o_header_overlay) .o_header_boxed_background {
|
||||||
@include o-apply-colors('header-boxed');
|
@include o-apply-colors('header-boxed');
|
||||||
|
@include o-apply-colors('header-boxed-custom');
|
||||||
|
@include o-add-gradient('header-boxed-gradient');
|
||||||
}
|
}
|
||||||
} @else if o-website-value('header-template') == 'centered_logo' {
|
} @else if o-website-value('header-template') == 'centered_logo' {
|
||||||
header .o_header_centered_logo {
|
header .o_header_centered_logo {
|
||||||
@@ -1280,8 +1292,8 @@ header {
|
|||||||
// Footer scrolltop button
|
// Footer scrolltop button
|
||||||
@if o-website-value('footer-scrolltop') {
|
@if o-website-value('footer-scrolltop') {
|
||||||
#o_footer_scrolltop {
|
#o_footer_scrolltop {
|
||||||
$-footer-color: o-color('footer');
|
$-footer-color: o-color('footer-custom') or o-color('footer');
|
||||||
$-copyright-color: o-color('copyright');
|
$-copyright-color: o-color('copyright-custom') or o-color('copyright');
|
||||||
$-copyright-color: mix(rgba($-copyright-color, 1.0), $-footer-color, percentage(alpha($-copyright-color)));
|
$-copyright-color: mix(rgba($-copyright-color, 1.0), $-footer-color, percentage(alpha($-copyright-color)));
|
||||||
|
|
||||||
box-sizing: content-box;
|
box-sizing: content-box;
|
||||||
@@ -1290,6 +1302,7 @@ header {
|
|||||||
border: 0;
|
border: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@include o-apply-colors('footer', $with-extras: false, $background: $-footer-color);
|
@include o-apply-colors('footer', $with-extras: false, $background: $-footer-color);
|
||||||
|
@include o-apply-colors('footer-custom', $with-extras: false, $background: $-footer-color);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
|
||||||
@if $-footer-color == $-copyright-color {
|
@if $-footer-color == $-copyright-color {
|
||||||
|
|||||||
@@ -0,0 +1,262 @@
|
|||||||
|
/** @odoo-module */
|
||||||
|
|
||||||
|
import tour from 'web_tour.tour';
|
||||||
|
import weUtils from 'web_editor.utils';
|
||||||
|
import wTourUtils from 'website.tour_utils';
|
||||||
|
|
||||||
|
const snippets = [
|
||||||
|
{
|
||||||
|
id: 's_text_image',
|
||||||
|
name: 'Text - Image',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const gradients = [
|
||||||
|
'linear-gradient(135deg, rgb(203, 94, 238) 0%, rgb(75, 225, 236) 100%)',
|
||||||
|
'linear-gradient(135deg, rgb(255, 222, 202) 0%, rgb(202, 115, 69) 100%)',
|
||||||
|
];
|
||||||
|
|
||||||
|
function checkAndUpdateBackgroundColor({
|
||||||
|
checkCC, checkNoCC,
|
||||||
|
checkBg, checkNoBg,
|
||||||
|
checkGradient, checkNoGradient,
|
||||||
|
changeType, change,
|
||||||
|
finalSelector, finalRun
|
||||||
|
}) {
|
||||||
|
const steps = [
|
||||||
|
wTourUtils.changeBackgroundColor(),
|
||||||
|
];
|
||||||
|
|
||||||
|
function typeToName(xType) {
|
||||||
|
return xType === 'cc' ? 'color combinations' : xType === 'bg' ? 'background colors' : 'gradients';
|
||||||
|
}
|
||||||
|
|
||||||
|
function switchTo(type, _name) {
|
||||||
|
const target = type === 'cc' ? 'color-combinations' : type === 'bg' ? 'custom-colors' : 'gradients';
|
||||||
|
const name = _name || typeToName(type);
|
||||||
|
steps.push({
|
||||||
|
trigger: `.o_we_colorpicker_switch_pane_btn[data-target="${target}"]`,
|
||||||
|
content: `Switch to ${name}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function check(checkX, checkNoX, xType, noSwitch = false) {
|
||||||
|
if (!checkX && !checkNoX) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = typeToName(xType);
|
||||||
|
const selectorCheckX = checkX && `.o_we_color_btn[data-color="${checkX}"].selected`;
|
||||||
|
const selectorCheckNoX = checkNoX && `.o_we_color_btn[data-color="${checkNoX}"]:not(.selected)`;
|
||||||
|
const step = {
|
||||||
|
trigger: selectorCheckX || selectorCheckNoX,
|
||||||
|
content: `The correct ${name} is marked as selected`,
|
||||||
|
position: 'bottom',
|
||||||
|
run: () => null,
|
||||||
|
};
|
||||||
|
if (!selectorCheckX && selectorCheckNoX) {
|
||||||
|
step.extra_trigger = selectorCheckNoX;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!noSwitch) {
|
||||||
|
switchTo(xType, name);
|
||||||
|
}
|
||||||
|
steps.push(step);
|
||||||
|
}
|
||||||
|
|
||||||
|
check(checkCC, checkNoCC, 'cc', true);
|
||||||
|
check(checkBg, checkNoBg, 'bg');
|
||||||
|
check(checkGradient, checkNoGradient, 'gradient');
|
||||||
|
|
||||||
|
if (changeType) {
|
||||||
|
switchTo(changeType);
|
||||||
|
steps.push(wTourUtils.changeOption('ColoredLevelBackground', `.o_we_color_btn[data-color="${change}"]`));
|
||||||
|
steps.push({
|
||||||
|
trigger: finalSelector,
|
||||||
|
content: "The selected colors have been applied (CC AND (BG or GRADIENT))",
|
||||||
|
position: 'bottom',
|
||||||
|
run: finalRun,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return steps;
|
||||||
|
}
|
||||||
|
|
||||||
|
tour.register('snippet_background_edition', {
|
||||||
|
test: true,
|
||||||
|
url: '/?enable_editor=1',
|
||||||
|
},
|
||||||
|
[
|
||||||
|
wTourUtils.dragNDrop(snippets[0]),
|
||||||
|
wTourUtils.clickOnSnippet(snippets[0]),
|
||||||
|
|
||||||
|
// Add a color combination
|
||||||
|
...checkAndUpdateBackgroundColor({
|
||||||
|
changeType: 'cc',
|
||||||
|
change: 3,
|
||||||
|
finalSelector: `.${snippets[0].id}.o_cc.o_cc3:not([class*=bg-]):not([style*="background"])`,
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Change the color combination + Check the previous one was marked as selected
|
||||||
|
...checkAndUpdateBackgroundColor({
|
||||||
|
checkCC: 3,
|
||||||
|
changeType: 'cc',
|
||||||
|
change: 2,
|
||||||
|
finalSelector: `.${snippets[0].id}.o_cc.o_cc2:not(.o_cc3):not([class*=bg-])`,
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Check the color combination was marked as selected + Edit the bg color
|
||||||
|
...checkAndUpdateBackgroundColor({
|
||||||
|
checkCC: 2,
|
||||||
|
checkNoCC: 3,
|
||||||
|
changeType: 'bg',
|
||||||
|
change: 'black-50',
|
||||||
|
finalSelector: `.${snippets[0].id}.o_cc.o_cc2.bg-black-50`,
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Check the current color palette selection + Change the bg color
|
||||||
|
...checkAndUpdateBackgroundColor({
|
||||||
|
checkCC: 2,
|
||||||
|
checkBg: 'black-50',
|
||||||
|
changeType: 'bg',
|
||||||
|
change: '800',
|
||||||
|
finalSelector: `.${snippets[0].id}.o_cc.o_cc2.bg-800:not(.bg-black-50)`,
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Check the current color palette selection + Change the color combination
|
||||||
|
// again. It should keep the bg color class.
|
||||||
|
...checkAndUpdateBackgroundColor({
|
||||||
|
checkCC: 2,
|
||||||
|
checkBg: '800',
|
||||||
|
checkNoBg: 'black-50',
|
||||||
|
changeType: 'cc',
|
||||||
|
change: 4,
|
||||||
|
finalSelector: `.${snippets[0].id}.o_cc.o_cc4:not(.o_cc2).bg-800`,
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Check the current color palette status + Replace the bg color by a gradient
|
||||||
|
...checkAndUpdateBackgroundColor({
|
||||||
|
checkCC: 4,
|
||||||
|
checkNoCC: 2,
|
||||||
|
checkBg: '800',
|
||||||
|
changeType: 'gradient',
|
||||||
|
change: gradients[0],
|
||||||
|
finalSelector: `.${snippets[0].id}.o_cc.o_cc4:not(.bg-800)[style*="background-image: ${gradients[0]}"]`,
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Check the current color palette status + Replace the gradient
|
||||||
|
...checkAndUpdateBackgroundColor({
|
||||||
|
checkCC: 4,
|
||||||
|
checkNoBg: '800',
|
||||||
|
checkGradient: gradients[0],
|
||||||
|
changeType: 'gradient',
|
||||||
|
change: gradients[1],
|
||||||
|
finalSelector: `.${snippets[0].id}.o_cc.o_cc4[style*="background-image: ${gradients[1]}"]:not([style*="background-image: ${gradients[0]}"])`,
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Check the current color palette selection + Change the color combination
|
||||||
|
// again. It should keep the gradient.
|
||||||
|
...checkAndUpdateBackgroundColor({
|
||||||
|
checkCC: 4,
|
||||||
|
checkGradient: gradients[1],
|
||||||
|
checkNoGradient: gradients[0],
|
||||||
|
changeType: 'cc',
|
||||||
|
change: 1,
|
||||||
|
finalSelector: `.${snippets[0].id}.o_cc.o_cc1:not(.o_cc4)[style*="background-image: ${gradients[1]}"]`,
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Final check of the color status in the color palette
|
||||||
|
...checkAndUpdateBackgroundColor({
|
||||||
|
checkCC: 1,
|
||||||
|
checkNoCC: 4,
|
||||||
|
checkGradient: gradients[1],
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Now, add an image on top of that color combination + gradient
|
||||||
|
wTourUtils.changeOption('ColoredLevelBackground', '[data-name="bg_image_toggle_opt"]'),
|
||||||
|
{
|
||||||
|
trigger: '.o_existing_attachment_cell img',
|
||||||
|
content: "Select an image in the media dialog",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
trigger: `.${snippets[0].id}.o_cc.o_cc1`,
|
||||||
|
run: function () {
|
||||||
|
const parts = weUtils.backgroundImageCssToParts(this.$anchor.css('background-image'));
|
||||||
|
if (!parts.url || !parts.url.startsWith('url(')) {
|
||||||
|
console.error('An image should have been added as background.');
|
||||||
|
}
|
||||||
|
if (parts.gradient !== gradients[1]) {
|
||||||
|
console.error('The gradient should have been kept when adding the background image');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Replace the gradient while there is a background-image
|
||||||
|
...checkAndUpdateBackgroundColor({
|
||||||
|
checkCC: 1,
|
||||||
|
checkGradient: gradients[1],
|
||||||
|
changeType: 'gradient',
|
||||||
|
change: gradients[0],
|
||||||
|
finalSelector: `.${snippets[0].id}.o_cc.o_cc1:not([style*="${gradients[1]}"])`,
|
||||||
|
finalRun: function () {
|
||||||
|
const parts = weUtils.backgroundImageCssToParts(this.$anchor.css('background-image'));
|
||||||
|
if (!parts.url || !parts.url.startsWith('url(')) {
|
||||||
|
console.error('The image should have been kept when changing the gradient');
|
||||||
|
}
|
||||||
|
if (parts.gradient !== gradients[0]) {
|
||||||
|
console.error('The gradient should have been changed');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Replace the gradient by a bg color
|
||||||
|
...checkAndUpdateBackgroundColor({
|
||||||
|
checkCC: 1,
|
||||||
|
checkGradient: gradients[0],
|
||||||
|
checkNoGradient: gradients[1],
|
||||||
|
changeType: 'bg',
|
||||||
|
change: 'black-75',
|
||||||
|
finalSelector: `.${snippets[0].id}.o_cc.o_cc1.bg-black-75[style^="background-image: url("]:not([style*="${gradients[0]}"])`
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Re-add a gradient
|
||||||
|
...checkAndUpdateBackgroundColor({
|
||||||
|
checkCC: 1,
|
||||||
|
checkBg: 'black-75',
|
||||||
|
checkNoGradient: gradients[0],
|
||||||
|
changeType: 'gradient',
|
||||||
|
change: gradients[1],
|
||||||
|
finalSelector: `.${snippets[0].id}.o_cc.o_cc1:not(.bg-black-75)`,
|
||||||
|
finalRun: function () {
|
||||||
|
const parts = weUtils.backgroundImageCssToParts(this.$anchor.css('background-image'));
|
||||||
|
if (!parts.url || !parts.url.startsWith('url(')) {
|
||||||
|
console.error('The image should have been kept when re-adding the gradient');
|
||||||
|
}
|
||||||
|
if (parts.gradient !== gradients[1]) {
|
||||||
|
console.error('The gradient should have been re-added');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Final check of color selection and removing the image
|
||||||
|
...checkAndUpdateBackgroundColor({
|
||||||
|
checkCC: 1,
|
||||||
|
checkNoBg: 'black-75',
|
||||||
|
checkGradient: gradients[1],
|
||||||
|
}),
|
||||||
|
wTourUtils.changeOption('ColoredLevelBackground', '[data-name="bg_image_toggle_opt"]'),
|
||||||
|
{
|
||||||
|
trigger: `.${snippets[0].id}.o_cc.o_cc1[style*="background-image: ${gradients[1]}"]`,
|
||||||
|
run: () => null,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Now removing all colors via the 'None' button (note: colorpicker still opened)
|
||||||
|
{
|
||||||
|
trigger: '.o_colorpicker_reset',
|
||||||
|
content: "Click on the None button of the color palette",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
trigger: `.${snippets[0].id}:not(.o_cc):not(.o_cc1):not([style*="background-image"])`,
|
||||||
|
content: "All color classes and properties should have been removed",
|
||||||
|
run: () => null,
|
||||||
|
}
|
||||||
|
]);
|
||||||
@@ -55,12 +55,6 @@ tour.register('snippet_empty_parent_autoremove', {
|
|||||||
trigger: '#wrap .s_banner .o_we_shape',
|
trigger: '#wrap .s_banner .o_we_shape',
|
||||||
run: () => null,
|
run: () => null,
|
||||||
},
|
},
|
||||||
wTourUtils.changeOption('ColoredLevelBackground', 'Filter'),
|
|
||||||
{
|
|
||||||
content: "Check that background-filter is present",
|
|
||||||
trigger: '#wrap .s_banner .o_we_bg_filter',
|
|
||||||
run: () => null,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
content: "Click on first column",
|
content: "Click on first column",
|
||||||
trigger: '#wrap .s_banner .row > :first-child',
|
trigger: '#wrap .s_banner .row > :first-child',
|
||||||
|
|||||||
@@ -206,3 +206,13 @@ class TestUi(odoo.tests.HttpCase):
|
|||||||
def test_10_website_conditional_visibility(self):
|
def test_10_website_conditional_visibility(self):
|
||||||
self.start_tour('/', 'conditional_visibility_1', login='admin')
|
self.start_tour('/', 'conditional_visibility_1', login='admin')
|
||||||
self.start_tour('/', 'conditional_visibility_2', login='admin')
|
self.start_tour('/', 'conditional_visibility_2', login='admin')
|
||||||
|
|
||||||
|
def test_11_website_snippet_background_edition(self):
|
||||||
|
self.env['ir.attachment'].create({
|
||||||
|
'public': True,
|
||||||
|
'type': 'url',
|
||||||
|
'url': '/web/image/123/test.png',
|
||||||
|
'name': 'test.png',
|
||||||
|
'mimetype': 'image/png',
|
||||||
|
})
|
||||||
|
self.start_tour('/', 'snippet_background_edition', login='admin')
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
<t t-set="target" t-value="'.s_blockquote_content'"/>
|
<t t-set="target" t-value="'.s_blockquote_content'"/>
|
||||||
<t t-set="with_colors" t-value="True"/>
|
<t t-set="with_colors" t-value="True"/>
|
||||||
<t t-set="with_images" t-value="True"/>
|
<t t-set="with_images" t-value="True"/>
|
||||||
|
<t t-set="with_gradients" t-value="True"/>
|
||||||
<t t-set="images_dependencies" t-value="'blockquote_cover_opt'"/>
|
<t t-set="images_dependencies" t-value="'blockquote_cover_opt'"/>
|
||||||
</t>
|
</t>
|
||||||
|
|
||||||
|
|||||||
@@ -62,6 +62,7 @@
|
|||||||
<t t-set="with_colors" t-value="True"/>
|
<t t-set="with_colors" t-value="True"/>
|
||||||
<t t-set="with_images" t-value="False"/>
|
<t t-set="with_images" t-value="False"/>
|
||||||
<t t-set="with_color_combinations" t-value="True"/>
|
<t t-set="with_color_combinations" t-value="True"/>
|
||||||
|
<t t-set="with_gradients" t-value="True"/>
|
||||||
</t>
|
</t>
|
||||||
<div data-js="Box" data-selector=".s_media_list_item" data-target="> .row">
|
<div data-js="Box" data-selector=".s_media_list_item" data-target="> .row">
|
||||||
<t t-call="website.snippet_options_border_widgets"/>
|
<t t-call="website.snippet_options_border_widgets"/>
|
||||||
|
|||||||
@@ -190,8 +190,8 @@
|
|||||||
<i class="fa fa-fw fa-film"/>
|
<i class="fa fa-fw fa-film"/>
|
||||||
</we-button>
|
</we-button>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//we-button[@data-toggle-bg-filter]" position="attributes">
|
<xpath expr="//t[@t-set='color_filter_dependencies']" position="after">
|
||||||
<attribute name="data-dependencies" add="bg_video_toggler_opt" separator=","/>
|
<t t-set="color_filter_dependencies" t-valuef="#{color_filter_dependencies}, bg_video_toggler_opt"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//div[@data-js='BackgroundOptimize']" position="after">
|
<xpath expr="//div[@data-js='BackgroundOptimize']" position="after">
|
||||||
<!-- Parallax -->
|
<!-- Parallax -->
|
||||||
@@ -384,6 +384,7 @@
|
|||||||
<t t-set="with_colors" t-value="True"/>
|
<t t-set="with_colors" t-value="True"/>
|
||||||
<t t-set="with_images" t-value="True"/>
|
<t t-set="with_images" t-value="True"/>
|
||||||
<t t-set="css_compatible" t-value="True"/>
|
<t t-set="css_compatible" t-value="True"/>
|
||||||
|
<t t-set="with_gradients" t-value="True"/>
|
||||||
</t>
|
</t>
|
||||||
<div data-js="Carousel"
|
<div data-js="Carousel"
|
||||||
data-selector="section"
|
data-selector="section"
|
||||||
@@ -481,6 +482,7 @@
|
|||||||
<t t-set="with_colors" t-value="True"/>
|
<t t-set="with_colors" t-value="True"/>
|
||||||
<t t-set="with_images" t-value="True"/>
|
<t t-set="with_images" t-value="True"/>
|
||||||
<t t-set="with_color_combinations" t-value="True"/>
|
<t t-set="with_color_combinations" t-value="True"/>
|
||||||
|
<t t-set="with_gradients" t-value="True"/>
|
||||||
</t>
|
</t>
|
||||||
|
|
||||||
<t t-call="web_editor.snippet_options_background_options">
|
<t t-call="web_editor.snippet_options_background_options">
|
||||||
@@ -489,6 +491,7 @@
|
|||||||
<t t-set="with_colors" t-value="True"/>
|
<t t-set="with_colors" t-value="True"/>
|
||||||
<t t-set="with_images" t-value="False"/>
|
<t t-set="with_images" t-value="False"/>
|
||||||
<t t-set="with_color_combinations" t-value="True"/>
|
<t t-set="with_color_combinations" t-value="True"/>
|
||||||
|
<t t-set="with_gradients" t-value="True"/>
|
||||||
</t>
|
</t>
|
||||||
|
|
||||||
<t t-call="web_editor.snippet_options_background_options">
|
<t t-call="web_editor.snippet_options_background_options">
|
||||||
@@ -552,7 +555,8 @@
|
|||||||
data-selector="[data-oe-expression='res_company.partner_id']"
|
data-selector="[data-oe-expression='res_company.partner_id']"
|
||||||
data-no-check="true"/>
|
data-no-check="true"/>
|
||||||
|
|
||||||
<div data-selector="#wrapwrap > header"
|
<div data-js="WebsiteLevelColor"
|
||||||
|
data-selector="#wrapwrap > header"
|
||||||
data-no-check="true"
|
data-no-check="true"
|
||||||
groups="website.group_website_designer">
|
groups="website.group_website_designer">
|
||||||
<we-select string="Template" data-variable="header-template" data-reload="/">
|
<we-select string="Template" data-variable="header-template" data-reload="/">
|
||||||
@@ -628,10 +632,18 @@
|
|||||||
|
|
||||||
<we-row string="Colors">
|
<we-row string="Colors">
|
||||||
<we-colorpicker data-customize-website-color="" data-color="menu"
|
<we-colorpicker data-customize-website-color="" data-color="menu"
|
||||||
data-with-combinations="true"/>
|
data-customize-website-layer2-color="" data-layer-color="menu-custom" data-layer-gradient="menu-gradient"
|
||||||
|
data-no-bundle-reload="true"
|
||||||
|
data-null-value="'NULL'"
|
||||||
|
data-with-combinations="customizeWebsiteColor"
|
||||||
|
data-with-gradients="true"/>
|
||||||
<we-colorpicker data-dependencies="header_boxed_opt"
|
<we-colorpicker data-dependencies="header_boxed_opt"
|
||||||
data-customize-website-color="" data-color="header-boxed"
|
data-customize-website-color="" data-color="header-boxed"
|
||||||
data-with-combinations="true"/>
|
data-customize-website-layer2-color="" data-layer-color="header-boxed-custom" data-layer-gradient="header-boxed-gradient"
|
||||||
|
data-no-bundle-reload="true"
|
||||||
|
data-null-value="'NULL'"
|
||||||
|
data-with-combinations="customizeWebsiteColor"
|
||||||
|
data-with-gradients="true"/>
|
||||||
</we-row>
|
</we-row>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -834,7 +846,8 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Footer - Colors & Layouts -->
|
<!-- Footer - Colors & Layouts -->
|
||||||
<div data-selector="#wrapwrap > footer"
|
<div data-js="WebsiteLevelColor"
|
||||||
|
data-selector="#wrapwrap > footer"
|
||||||
data-no-check="true"
|
data-no-check="true"
|
||||||
groups="website.group_website_designer">
|
groups="website.group_website_designer">
|
||||||
|
|
||||||
@@ -878,7 +891,11 @@
|
|||||||
<!-- Colors -->
|
<!-- Colors -->
|
||||||
<we-colorpicker string="Colors"
|
<we-colorpicker string="Colors"
|
||||||
data-customize-website-color="" data-color="footer"
|
data-customize-website-color="" data-color="footer"
|
||||||
data-with-combinations="true"/>
|
data-customize-website-layer2-color="" data-layer-color="footer-custom" data-layer-gradient="footer-gradient"
|
||||||
|
data-no-bundle-reload="true"
|
||||||
|
data-null-value="'NULL'"
|
||||||
|
data-with-combinations="customizeWebsiteColor"
|
||||||
|
data-with-gradients="true"/>
|
||||||
<we-select string="Slideout Effect" data-variable="footer-effect" data-reload="/">
|
<we-select string="Slideout Effect" data-variable="footer-effect" data-reload="/">
|
||||||
<we-button string="Regular"
|
<we-button string="Regular"
|
||||||
data-customize-website-views=""
|
data-customize-website-views=""
|
||||||
@@ -941,12 +958,17 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Copyright -->
|
<!-- Copyright -->
|
||||||
<div data-selector=".o_footer_copyright"
|
<div data-js="WebsiteLevelColor"
|
||||||
|
data-selector=".o_footer_copyright"
|
||||||
data-no-check="true"
|
data-no-check="true"
|
||||||
groups="website.group_website_designer">
|
groups="website.group_website_designer">
|
||||||
<we-colorpicker string="Colors"
|
<we-colorpicker string="Colors"
|
||||||
data-customize-website-color="" data-color="copyright"
|
data-customize-website-color="" data-color="copyright"
|
||||||
data-with-combinations="true"/>
|
data-customize-website-layer2-color="" data-layer-color="copyright-custom" data-layer-gradient="copyright-gradient"
|
||||||
|
data-no-bundle-reload="true"
|
||||||
|
data-null-value="'NULL'"
|
||||||
|
data-with-combinations="customizeWebsiteColor"
|
||||||
|
data-with-gradients="true"/>
|
||||||
<we-select string="Language Selector" data-reload="/">
|
<we-select string="Language Selector" data-reload="/">
|
||||||
<we-button data-name="language_selector_none_opt"
|
<we-button data-name="language_selector_none_opt"
|
||||||
data-customize-website-views="">None</we-button>
|
data-customize-website-views="">None</we-button>
|
||||||
@@ -985,6 +1007,7 @@
|
|||||||
<we-row string="Background" class="o_we_full_row">
|
<we-row string="Background" class="o_we_full_row">
|
||||||
<t t-call="web_editor.snippet_options_background_color_widget">
|
<t t-call="web_editor.snippet_options_background_color_widget">
|
||||||
<t t-set="with_color_combinations" t-value="True"/>
|
<t t-set="with_color_combinations" t-value="True"/>
|
||||||
|
<t t-set="with_gradients" t-value="True"/>
|
||||||
</t>
|
</t>
|
||||||
<we-button-group class="ml-auto">
|
<we-button-group class="ml-auto">
|
||||||
<we-imagepicker title="Image" data-background="" data-button-style="true"/>
|
<we-imagepicker title="Image" data-background="" data-button-style="true"/>
|
||||||
|
|||||||
Reference in New Issue
Block a user