diff options
| author | Samuel Degueldre <sad@odoo.com> | 2020-07-31 06:47:34 +0000 |
|---|---|---|
| committer | qsm-odoo <qsm@odoo.com> | 2020-08-07 15:34:45 +0000 |
| commit | b84e0af742c51b88b4c108ebec2d0c7fff4b7483 (patch) | |
| tree | 5f26347b2ffc0dd30ddbe6193c21218a9e922c91 /addons/web_editor/static/src | |
| parent | c3dacfea05c531683bfcf94243597ce2d82aeab8 (diff) | |
[IMP] web_editor, website: add background shapes snippet option
This commit adds a new snippet option allowing the user to choose one of
a series of SVG shapes as their background, or to be overlaid over the
background image of a snippet and to customize the colors of those
shapes.
Part of https://github.com/odoo/odoo/pull/53017
task-2210790
closes odoo/odoo#53017
Signed-off-by: Quentin Smetz (qsm) <qsm@odoo.com>
Diffstat (limited to 'addons/web_editor/static/src')
5 files changed, 612 insertions, 43 deletions
diff --git a/addons/web_editor/static/src/js/common/utils.js b/addons/web_editor/static/src/js/common/utils.js index aaa02c58589..c66bb75a47c 100644 --- a/addons/web_editor/static/src/js/common/utils.js +++ b/addons/web_editor/static/src/js/common/utils.js @@ -200,6 +200,20 @@ function _normalizeColor(color) { } return _getCSSVariableValue(color); } +/** + * Parse an element's background-image's url. + * + * @param {string} string a css value in the form 'url("...")' + * @returns {string|false} the src of the image or false if not parsable + */ +function _getBgImageURL(el) { + const string = $(el).css('background-image'); + const match = string.match(/^url\((['"])(.*?)\1\)$/); + if (!match) { + return ''; + } + return match[2]; +} return { CSS_SHORTHANDS: CSS_SHORTHANDS, @@ -213,5 +227,6 @@ return { computeColorClasses: _computeColorClasses, getCSSVariableValue: _getCSSVariableValue, normalizeColor: _normalizeColor, + getBgImageURL: _getBgImageURL, }; }); diff --git a/addons/web_editor/static/src/js/editor/snippets.options.js b/addons/web_editor/static/src/js/editor/snippets.options.js index 1060140c619..e752d07f66c 100644 --- a/addons/web_editor/static/src/js/editor/snippets.options.js +++ b/addons/web_editor/static/src/js/editor/snippets.options.js @@ -8,7 +8,10 @@ const time = require('web.time'); var Widget = require('web.Widget'); var ColorPaletteWidget = require('web_editor.ColorPalette').ColorPaletteWidget; const weUtils = require('web_editor.utils'); -const {normalizeColor} = weUtils; +const { + normalizeColor, + getBgImageURL, +} = weUtils; var weWidgets = require('wysiwyg.widgets'); const { loadImage, @@ -1588,6 +1591,87 @@ const RangeUserValueWidget = UserValueWidget.extend({ }, }); +const SelectPagerUserValueWidget = SelectUserValueWidget.extend({ + className: (SelectUserValueWidget.prototype.className || '') + ' o_we_select_pager', + events: Object.assign({}, SelectUserValueWidget.prototype.events, { + 'click .o_we_pager_next, .o_we_pager_prev': '_onPageChange', + }), + + /** + * @override + */ + async start() { + const _super = this._super.bind(this); + this.pages = this.options.childNodes.filter(node => node.matches && node.matches('we-select-page')); + this.numPages = this.pages.length; + + const prev = document.createElement('i'); + prev.classList.add('o_we_pager_prev', 'fa', 'fa-chevron-left'); + + this.pageNum = document.createElement('span'); + this.currentPage = 0; + this._updatePageNum(); + + const next = document.createElement('i'); + next.classList.add('o_we_pager_next', 'fa', 'fa-chevron-right'); + + const pagerControls = document.createElement('div'); + pagerControls.classList.add('o_we_pager_controls'); + pagerControls.appendChild(prev); + pagerControls.appendChild(this.pageNum); + pagerControls.appendChild(next); + + await _super(...arguments); + this.menuEl.classList.add('o_we_has_pager'); + $(this.menuEl).prepend(pagerControls); + }, + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * Updates the pager's page number display. + * + * @private + */ + _updatePageNum() { + this.pages.forEach((page, i) => page.classList.toggle('active', i === this.currentPage)); + this.pageNum.textContent = `${this.currentPage + 1}/${this.numPages}`; + }, + + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + + /** + * Goes to the previous/next page with wrap-around. + * + * @private + */ + _onPageChange(ev) { + ev.preventDefault(); + ev.stopPropagation(); + const delta = ev.target.matches('.o_we_pager_next') ? 1 : -1; + this.currentPage = (this.currentPage + this.numPages + delta) % this.numPages; + this._updatePageNum(); + }, + /** + * @override + */ + _onClick(ev) { + const activeButton = this._getActiveSubWidget(); + if (activeButton) { + const currentPage = this.pages.indexOf(activeButton.el.closest('we-select-page')); + if (currentPage !== -1) { + this.currentPage = currentPage; + this._updatePageNum(); + } + } + return this._super(...arguments); + }, +}); + const userValueWidgetsRegistry = { 'we-button': ButtonUserValueWidget, 'we-checkbox': CheckboxUserValueWidget, @@ -1600,6 +1684,7 @@ const userValueWidgetsRegistry = { 'we-imagepicker': ImagepickerUserValueWidget, 'we-videopicker': VideopickerUserValueWidget, 'we-range': RangeUserValueWidget, + 'we-select-pager': SelectPagerUserValueWidget, }; /** @@ -3087,18 +3172,6 @@ registry.ImageOptimize = ImageHandlerOption.extend({ }); /** - * Returns the src value from a css value related to a background image - * (e.g. "url('blabla')" => "blabla" / "none" => ""). - * - * @param {string} value - * @returns {string} - */ -const getSrcFromCssValue = value => { - var srcValueWrapper = /url\(['"]*|['"]*\)|^none$/g; - return value && value.replace(srcValueWrapper, '') || ''; -}; - -/** * Controls background image width and quality. */ registry.BackgroundOptimize = ImageHandlerOption.extend({ @@ -3166,9 +3239,9 @@ registry.BackgroundOptimize = ImageHandlerOption.extend({ Object.entries(this.$target[0].dataset).forEach(([key, value]) => { this.img.dataset[key] = value; }); - const src = new URL(getSrcFromCssValue(this.$target.css('background-image')), window.location.origin); + const src = new URL(getBgImageURL(this.$target[0]), window.location.origin); // Make URL relative because that is how image urls are stored in the database. - this.img.src = src.origin === window.location.origin ? src.pathname : src; + this.img.src = src.origin === window.location.origin && src.pathname; return await this._super(...arguments); }, @@ -3213,6 +3286,18 @@ registry.BackgroundToggler = SnippetOptionWidget.extend({ this._requestUserValueWidgets('bg_image_opt')[0].el.click(); } }, + /** + * Toggles background shape on or off. + * + * @see this.selectClass for parameters + */ + toggleBgShape(previewMode, widgetValue, params) { + const [shapeWidget] = this._requestUserValueWidgets('bg_shape_opt'); + const shapeOption = shapeWidget.getParent(); + // TODO: open select after shape was selected? + // TODO: use setWidgetValue instead of calling shapeOption method directly when possible + return shapeOption._toggleShape(); + }, //-------------------------------------------------------------------------- // Private @@ -3222,10 +3307,17 @@ registry.BackgroundToggler = SnippetOptionWidget.extend({ * @override */ _computeWidgetState(methodName, params) { - if (methodName === 'toggleBgImage') { - const [bgImageWidget] = this._requestUserValueWidgets('bg_image_opt'); - const bgImageOpt = bgImageWidget.getParent(); - return !!bgImageOpt._computeWidgetState('background', bgImageWidget.getMethodsParams('background')); + switch (methodName) { + case 'toggleBgImage': { + const [bgImageWidget] = this._requestUserValueWidgets('bg_image_opt'); + const bgImageOpt = bgImageWidget.getParent(); + return !!bgImageOpt._computeWidgetState('background', bgImageWidget.getMethodsParams('background')); + } + case 'toggleBgShape': { + const [shapeWidget] = this._requestUserValueWidgets('bg_shape_opt'); + const shapeOption = shapeWidget.getParent(); + return !!shapeOption._computeWidgetState('shape', shapeWidget.getMethodsParams('shape')); + } } return this._super(...arguments); }, @@ -3239,7 +3331,7 @@ registry.BackgroundImage = SnippetOptionWidget.extend({ * @override */ start: function () { - this.__customImageSrc = this._getSrcFromCssValue(); + this.__customImageSrc = getBgImageURL(this.$target[0]); return this._super(...arguments); }, @@ -3254,7 +3346,7 @@ registry.BackgroundImage = SnippetOptionWidget.extend({ */ background: async function (previewMode, widgetValue, params) { if (previewMode === true) { - this.__customImageSrc = this._getSrcFromCssValue(); + this.__customImageSrc = getBgImageURL(this.$target[0]); } else if (previewMode === 'reset') { widgetValue = this.__customImageSrc; } else { @@ -3285,7 +3377,7 @@ registry.BackgroundImage = SnippetOptionWidget.extend({ setTarget: function () { this._super(...arguments); // TODO should be automatic for all options as equal to the start method - this.__customImageSrc = this._getSrcFromCssValue(); + this.__customImageSrc = getBgImageURL(this.$target[0]); }, //-------------------------------------------------------------------------- @@ -3293,23 +3385,309 @@ registry.BackgroundImage = SnippetOptionWidget.extend({ //-------------------------------------------------------------------------- /** - * Returns the background image's src. + * @override + */ + _computeWidgetState: function (methodName) { + if (methodName === 'background') { + return getBgImageURL(this.$target[0]); + } + return this._super(...arguments); + }, +}); + +/** + * Handles background shapes. + */ +registry.BackgroundShape = SnippetOptionWidget.extend({ + /** + * @override + */ + updateUI() { + if (this.rerender) { + this.rerender = false; + return this._rerenderXML(); + } + return this._super.apply(this, arguments); + }, + + //-------------------------------------------------------------------------- + // Options + //-------------------------------------------------------------------------- + + /** + * Sets the current background shape. * - * @private - * @returns {string} + * @see this.selectClass for params + */ + shape(previewMode, widgetValue, params) { + this._handlePreviewState(previewMode, () => { + return {shape: widgetValue, colors: this._getDefaultColors(), flip: []}; + }); + }, + /** + * Sets the current background shape's colors. + * + * @see this.selectClass for params + */ + color(previewMode, widgetValue, params) { + this._handlePreviewState(previewMode, () => { + const {colorName} = params; + const {colors: previousColors} = this._getShapeData(); + const newColor = widgetValue || this._getDefaultColors()[colorName]; + const newColors = Object.assign(previousColors, {[colorName]: newColor}); + return {colors: newColors}; + }); + }, + /** + * Flips the shape on its x axis. + * + * @see this.selectClass for params */ - _getSrcFromCssValue: function () { - return getSrcFromCssValue(this.$target.css('background-image')); + flipX(previewMode, widgetValue, params) { + this._flipShape(previewMode, 'x'); }, /** + * Flips the shape on its y axis. + * + * @see this.selectClass for params + */ + flipY(previewMode, widgetValue, params) { + this._flipShape(previewMode, 'y'); + }, + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** * @override */ - _computeWidgetState: function (methodName) { - if (methodName === 'background') { - return this._getSrcFromCssValue(); + _computeWidgetState(methodName, params) { + switch (methodName) { + case 'shape': { + return this._getShapeData().shape; + } + case 'color': { + const {shape, colors: customColors} = this._getShapeData(); + const colors = Object.assign(this._getDefaultColors(), customColors); + const color = shape && colors[params.colorName]; + return color ? normalizeColor(color) : ''; + } + case 'flipX': { + return this.$target.find('> .o_we_shape.o_we_flip_x').length !== 0; + } + case 'flipY': { + return this.$target.find('> .o_we_shape.o_we_flip_y').length !== 0; + } + } + return this._super(...arguments); + }, + /** + * @override + */ + _renderCustomXML(uiFragment) { + Object.keys(this._getDefaultColors()).map(colorName => { + uiFragment.querySelector('[data-name="colors"]') + .prepend($(`<we-colorpicker data-color="true" data-color-name="${colorName}">`)[0]); + }); + + uiFragment.querySelectorAll('we-select-pager we-button[data-shape]').forEach(btn => { + const btnContent = document.createElement('div'); + btnContent.classList.add('o_we_shape_btn_content', 'position-relative', 'border-dark'); + const btnContentInnerDiv = document.createElement('div'); + btnContentInnerDiv.classList.add('o_we_shape'); + btnContent.appendChild(btnContentInnerDiv); + + const {shape} = btn.dataset; + const shapeEl = btnContent.querySelector('.o_we_shape'); + shapeEl.classList.add(`o_${shape.replace(/\//g, '_')}`); + btn.append(btnContent); + }); + return uiFragment; + }, + /** + * @override + */ + async _computeWidgetVisibility(widgetName, params) { + if (widgetName === 'shape_none_opt') { + return false; } return this._super(...arguments); }, + /** + * Flips the shape on its x/y axis. + * + * @param {boolean} previewMode + * @param {'x'|'y'} axis the axis of the shape that should be flipped. + */ + _flipShape(previewMode, axis) { + this._handlePreviewState(previewMode, () => { + const flip = new Set(this._getShapeData().flip); + if (flip.has(axis)) { + flip.delete(axis); + } else { + flip.add(axis); + } + return {flip: [...flip]}; + }); + }, + /** + * Handles everything related to saving state before preview and restoring + * it after a preview or locking in the changes when not in preview. + * + * @param {boolean} previewMode + * @param {function} computeShapeData function to compute the new shape data. + */ + _handlePreviewState(previewMode, computeShapeData) { + if (previewMode === 'reset') { + if (this.prevShape) { + this.$target[0].dataset.oeShapeData = this.prevShape; + } else { + delete this.$target[0].dataset.oeShapeData; + } + } else { + if (previewMode === true) { + this.prevShape = this.$target[0].dataset.oeShapeData; + } + const newShapeData = computeShapeData(); + this._markShape(newShapeData); + if (previewMode === false) { + this.prevShape = this.$target[0].dataset.oeShapeData; + if (newShapeData.hasOwnProperty('shape')) { + // Need to rerender for correct number of colorpickers + this.rerender = true; + } + } + } + + // Updates/removes the shape container as needed and gives it the + // correct background shape + const json = this.$target[0].dataset.oeShapeData; + const {shape, colors, flip = []} = json ? JSON.parse(json) : {}; + const target = this.$target[0]; + let shapeContainer = target.querySelector(':scope > .o_we_shape'); + if (!shape) { + if (shapeContainer) { + shapeContainer.remove(); + } + return; + } + if (!shapeContainer) { + shapeContainer = document.createElement('div'); + target.prepend(shapeContainer); + if (shapeContainer.nextElementSibling.matches('.s_parallax_bg')) { + // Move parallax before shape so it shows on top. + target.prepend(shapeContainer.nextElementSibling); + } + target.style.position = 'relative'; + } + shapeContainer.className = `o_we_shape o_${shape.replace(/\//g, '_')}`; + flip.forEach(dir => { + shapeContainer.classList.add(`o_we_flip_${dir}`); + }); + // Custom colors, overwrite shape that is set by the class + $(shapeContainer).css('background-image', colors ? `url("${this._getShapeSrc()}")` : ''); + }, + /** + * Overwrites shape properties with the specified data. + * + * @private + * @param {Object} newData an object with the new data + */ + _markShape(newData) { + const defaultColors = this._getDefaultColors(); + const shapeData = Object.assign(this._getShapeData(), newData); + const areColorsDefault = Object.entries(shapeData.colors).every(([colorName, colorValue]) => { + return colorValue === defaultColors[colorName]; + }); + if (areColorsDefault) { + delete shapeData.colors; + } + if (!shapeData.shape) { + delete this.$target[0].dataset.oeShapeData; + } else { + this.$target[0].dataset.oeShapeData = JSON.stringify(shapeData); + } + }, + /** + * Returns the src of the shape corresponding to the current parameters. + * + * @private + */ + _getShapeSrc() { + const {shape, colors} = this._getShapeData(); + if (!shape) { + return ''; + } + const queryString = Object.entries(colors) + .map(([colorName, colorValue]) => { + const encodedCol = encodeURIComponent(normalizeColor(colorValue)); + return `${colorName}=${encodedCol}`; + }).join('&'); + return `/web_editor/shape/${shape}.svg?${queryString}`; + }, + /** + * Retrieves current shape data from the target's dataset. + * + * @private + * @param {HTMLElement} [target=this.$target[0]] the target on which to read + * the shape data. + */ + _getShapeData(target = this.$target[0]) { + const defaultData = { + shape: '', + colors: this._getDefaultColors(), + flip: [], + }; + const json = target.dataset.oeShapeData; + return json ? Object.assign(defaultData, JSON.parse(json)) : defaultData; + }, + /** + * Returns the default colors for the currently selected shape. + * + * @private + */ + _getDefaultColors() { + const $shapeContainer = this.$target.find('> .o_we_shape') + .clone() + .addClass('d-none') + // Needs to be in document for bg-image class to take effect + .appendTo(document.body); + const shapeContainer = $shapeContainer[0]; + $shapeContainer.css('background-image', ''); + const shapeSrc = shapeContainer && getBgImageURL(shapeContainer); + $shapeContainer.remove(); + if (!shapeSrc) { + return {}; + } + const url = new URL(shapeSrc, window.location.origin); + return Object.fromEntries(url.searchParams.entries()); + }, + /** + * Toggles whether there is a shape or not, to be called from bg toggler. + * + * @private + */ + _toggleShape() { + if (this._getShapeData().shape) { + return this._handlePreviewState(false, () => ({shape: ''})); + } else { + const target = this.$target[0]; + const previousSibling = target.previousElementSibling; + const [shapeWidget] = this._requestUserValueWidgets('bg_shape_opt'); + const possibleShapes = shapeWidget.getMethodsParams('shape').possibleValues; + let shapeToSelect; + if (previousSibling) { + const previousShape = this._getShapeData(previousSibling).shape; + shapeToSelect = possibleShapes.find((shape, i) => { + return possibleShapes[i - 1] === previousShape; + }); + } else { + shapeToSelect = possibleShapes[1]; + } + return this._handlePreviewState(false, () => ({shape: shapeToSelect})); + } + }, }); /** @@ -3364,7 +3742,7 @@ registry.BackgroundPosition = SnippetOptionWidget.extend({ await new Promise(resolve => { this.img = document.createElement('img'); this.img.addEventListener('load', () => resolve()); - this.img.src = this._getSrcFromCssValue(); + this.img.src = getBgImageURL(this.$target[0]); }); const position = this.$target.css('background-position').split(' ').map(v => parseInt(v)); @@ -3399,7 +3777,7 @@ registry.BackgroundPosition = SnippetOptionWidget.extend({ * @override */ _computeVisibility: function () { - return this._super(...arguments) && (this.$target.css('background-image') !== 'none'); + return this._super(...arguments) && !!getBgImageURL(this.$target[0]); }, /** * @override @@ -3507,15 +3885,6 @@ registry.BackgroundPosition = SnippetOptionWidget.extend({ window.setTimeout(() => $(document).on('click.bgposition', this._onDocumentClicked.bind(this)), 0); }, /** - * Returns the background image's src. - * - * @private - * @returns {string} - */ - _getSrcFromCssValue: function () { - return getSrcFromCssValue(this.$target.css('background-image')); - }, - /** * Returns the difference between the target's size and the background's * rendered size. Background position values in % are a percentage of this. * diff --git a/addons/web_editor/static/src/scss/web_editor.frontend.scss b/addons/web_editor/static/src/scss/web_editor.frontend.scss index a1d057316f8..172ab7fbe22 100644 --- a/addons/web_editor/static/src/scss/web_editor.frontend.scss +++ b/addons/web_editor/static/src/scss/web_editor.frontend.scss @@ -31,3 +31,50 @@ } } } + +// Background shapes +[data-oe-shape-data] > * { + position: relative; +} + +.o_we_shape { + @include o-position-absolute(0,0,0,0); + // position: absolute needs to be !important otherwise + // some classes (video background) override it with position: relative + position: absolute !important; + background-repeat: no-repeat; + + @each $module, $shapes in $o-bg-shapes { + @each $shape, $style in $shapes { + $url-params: ''; + $colors: map-get($style, 'colors'); + @each $i in $colors { + // %23 is the url-encoded form of '#' which doesn't work as is in urls. + $color: str-replace("#{map-get($o-color-palette, "o-color-#{$i}")}", '#', '%23'); + $url-params: '#{$url-params}&c#{$i}=#{$color}'; + } + + // eg: o_website_shape_bg_1 + &.o_#{$module}_#{str-replace($shape, '/', '_')} { + // When not customized, this URL, built in SCSS, allows for the + // shape to be dynamic and adapted if future color changes. + background-image: url("/web_editor/shape/#{$module}/#{$shape}.svg?#{str-slice($url-params, 2)}"); + background-position: map-get($style, 'position'); + background-size: map-get($style, 'size'); + @if map-get($style, 'repeat-y') { + background-repeat: repeat-y; + } + } + } + } +} + +.o_we_flip_x { + transform: scaleX(-1); +} +.o_we_flip_y { + transform: scaleY(-1); +} +.o_we_flip_x.o_we_flip_y { + transform: scale(-1); +} diff --git a/addons/web_editor/static/src/scss/web_editor.variables.scss b/addons/web_editor/static/src/scss/web_editor.variables.scss index f71d4d36184..0c483f9d668 100644 --- a/addons/web_editor/static/src/scss/web_editor.variables.scss +++ b/addons/web_editor/static/src/scss/web_editor.variables.scss @@ -578,3 +578,101 @@ $o-color-extras-nesting-selector: '&, .o_colored_level &'; } } } + +// format: (module_name: (shape_filename: ('position': X, 'size': Y, 'colors': (1, [3], ...)), ...)) +$o-bg-shapes: ('web_editor': ( + 'Airy/01': ('position': bottom, 'size': 100% auto, 'colors': (1), 'repeat-y': false), + 'Airy/02': ('position': top, 'size': 100% auto, 'colors': (1), 'repeat-y': false), + 'Airy/03': ('position': top, 'size': 100% auto, 'colors': (5), 'repeat-y': false), + 'Airy/04': ('position': center, 'size': 100% 100%, 'colors': (1), 'repeat-y': false), + 'Airy/05': ('position': center, 'size': 100% 100%, 'colors': (1), 'repeat-y': false), + 'Airy/06': ('position': bottom, 'size': 100% auto, 'colors': (2), 'repeat-y': false), + 'Airy/07': ('position': top, 'size': 100% auto, 'colors': (2), 'repeat-y': false), + 'Airy/08': ('position': bottom, 'size': 100% auto, 'colors': (1), 'repeat-y': false), + 'Airy/09': ('position': top, 'size': 100% auto, 'colors': (1), 'repeat-y': false), + 'Airy/10': ('position': bottom, 'size': 100% auto, 'colors': (5), 'repeat-y': false), + 'Airy/11': ('position': top, 'size': 100% auto, 'colors': (5), 'repeat-y': false), + 'Airy/12': ('position': top, 'size': 100% auto, 'colors': (1, 3), 'repeat-y': false), + 'Airy/13': ('position': bottom, 'size': 100% auto, 'colors': (1, 4), 'repeat-y': false), + 'Airy/14': ('position': bottom, 'size': 100% auto, 'colors': (1, 4), 'repeat-y': false), + 'Blobs/01': ('position': top, 'size': 100% auto, 'colors': (2), 'repeat-y': false), + 'Blobs/02': ('position': center, 'size': 100% 100%, 'colors': (1, 2), 'repeat-y': false), + 'Blobs/03': ('position': top, 'size': 100% auto, 'colors': (2), 'repeat-y': false), + 'Blobs/04': ('position': center, 'size': 100% auto, 'colors': (5), 'repeat-y': false), + 'Blobs/05': ('position': bottom, 'size': 100% auto, 'colors': (1), 'repeat-y': false), + 'Blobs/06': ('position': top, 'size': 100% auto, 'colors': (1), 'repeat-y': false), + 'Blobs/07': ('position': top, 'size': 100% auto, 'colors': (5), 'repeat-y': false), + 'Blobs/08': ('position': right, 'size': 100% auto, 'colors': (1), 'repeat-y': false), + 'Blobs/09': ('position': bottom, 'size': 100% auto, 'colors': (3), 'repeat-y': false), + 'Blobs/10': ('position': top, 'size': 100% auto, 'colors': (1, 5), 'repeat-y': false), + 'Blobs/11': ('position': center, 'size': 100% auto, 'colors': (1), 'repeat-y': false), + 'Blobs/12': ('position': bottom, 'size': 100% auto, 'colors': (1), 'repeat-y': false), + 'Blocks/01': ('position': bottom, 'size': 100% auto, 'colors': (1, 3, 5), 'repeat-y': false), + 'Blocks/02': ('position': top, 'size': 100% auto, 'colors': (1, 3, 5), 'repeat-y': false), + 'Blocks/03': ('position': bottom, 'size': 100% auto, 'colors': (1, 4), 'repeat-y': false), + 'Blocks/04': ('position': bottom, 'size': 100% auto, 'colors': (1, 2, 3, 5), 'repeat-y': false), + 'Bold/01': ('position': top, 'size': 100% auto, 'colors': (2), 'repeat-y': false), + 'Bold/02': ('position': bottom, 'size': 100% auto, 'colors': (1, 2, 3), 'repeat-y': false), + 'Bold/03': ('position': bottom, 'size': 100% auto, 'colors': (1, 3, 5), 'repeat-y': false), + 'Bold/04': ('position': top, 'size': 100% auto, 'colors': (2, 3), 'repeat-y': false), + 'Bold/05': ('position': center, 'size': 100% auto, 'colors': (5), 'repeat-y': false), + 'Bold/06': ('position': center, 'size': 100% auto, 'colors': (5), 'repeat-y': false), + 'Bold/07': ('position': bottom, 'size': 100% auto, 'colors': (1, 2), 'repeat-y': false), + 'Bold/08': ('position': top, 'size': 100% auto, 'colors': (1), 'repeat-y': false), + 'Bold/09': ('position': bottom, 'size': 100% auto, 'colors': (2, 3), 'repeat-y': false), + 'Bold/10': ('position': top, 'size': 100% auto, 'colors': (1, 3, 4, 5), 'repeat-y': false), + 'Bold/11': ('position': bottom, 'size': 100% auto, 'colors': (1, 2, 3), 'repeat-y': false), + 'Bold/12': ('position': center, 'size': 100% auto, 'colors': (1, 2, 5), 'repeat-y': false), + 'Origins/01': ('position': bottom, 'size': 100% auto, 'colors': (2, 5), 'repeat-y': false), + 'Origins/02': ('position': bottom, 'size': 100% auto, 'colors': (3), 'repeat-y': false), + 'Origins/03': ('position': top, 'size': 100% auto, 'colors': (3), 'repeat-y': false), + 'Origins/04': ('position': bottom, 'size': 100% auto, 'colors': (3), 'repeat-y': false), + 'Origins/05': ('position': top, 'size': 100% auto, 'colors': (3), 'repeat-y': false), + 'Origins/06': ('position': center, 'size': 100% auto, 'colors': (3), 'repeat-y': false), + 'Origins/07': ('position': center, 'size': 100% 100%, 'colors': (3), 'repeat-y': false), + 'Origins/08': ('position': bottom, 'size': 100% auto, 'colors': (3), 'repeat-y': false), + 'Origins/09': ('position': top, 'size': 100% auto, 'colors': (1, 5), 'repeat-y': false), + 'Origins/10': ('position': bottom, 'size': 100% auto, 'colors': (2, 5), 'repeat-y': false), + 'Origins/11': ('position': bottom, 'size': 100% auto, 'colors': (3, 5), 'repeat-y': false), + 'Origins/12': ('position': top, 'size': 100% auto, 'colors': (3, 5), 'repeat-y': false), + 'Origins/13': ('position': center, 'size': 100% auto, 'colors': (3, 5), 'repeat-y': false), + 'Origins/14': ('position': bottom, 'size': 100% auto, 'colors': (4), 'repeat-y': false), + 'Origins/15': ('position': top, 'size': 100% auto, 'colors': (4), 'repeat-y': false), + 'Rainy/01': ('position': bottom, 'size': 100% auto, 'colors': (1, 5), 'repeat-y': false), + 'Rainy/02': ('position': top, 'size': 100% auto, 'colors': (1, 4, 5), 'repeat-y': false), + 'Rainy/03': ('position': top, 'size': 100% auto, 'colors': (2, 4, 5), 'repeat-y': true), + 'Rainy/04': ('position': top, 'size': 100% auto, 'colors': (1, 5), 'repeat-y': false), + 'Rainy/05': ('position': top, 'size': 100% auto, 'colors': (1, 5), 'repeat-y': false), + 'Rainy/06': ('position': bottom, 'size': 100% auto, 'colors': (1, 2, 3), 'repeat-y': false), + 'Rainy/07': ('position': top, 'size': 100% auto, 'colors': (1, 2, 3), 'repeat-y': false), + 'Rainy/08': ('position': top, 'size': 100% auto, 'colors': (1, 4), 'repeat-y': false), + 'Rainy/09': ('position': top, 'size': 100% auto, 'colors': (1), 'repeat-y': false), + 'Wavy/01': ('position': bottom, 'size': 100% auto, 'colors': (4), 'repeat-y': false), + 'Wavy/02': ('position': top, 'size': 100% auto, 'colors': (4), 'repeat-y': false), + 'Wavy/03': ('position': top, 'size': 100% auto, 'colors': (1, 2), 'repeat-y': false), + 'Wavy/04': ('position': bottom, 'size': 100% auto, 'colors': (1, 5), 'repeat-y': false), + 'Wavy/05': ('position': top, 'size': 100% auto, 'colors': (1, 5), 'repeat-y': false), + 'Wavy/06': ('position': top, 'size': 100% auto, 'colors': (1, 3, 4, 5), 'repeat-y': false), + 'Wavy/07': ('position': top, 'size': 100% auto, 'colors': (3), 'repeat-y': false), + 'Wavy/08': ('position': top, 'size': 100% auto, 'colors': (2), 'repeat-y': false), + 'Wavy/09': ('position': bottom, 'size': 100% auto, 'colors': (1, 5), 'repeat-y': false), + 'Wavy/10': ('position': center, 'size': 100% auto, 'colors': (1, 2), 'repeat-y': false), + 'Wavy/11': ('position': bottom, 'size': 100% auto, 'colors': (1, 4), 'repeat-y': false), + 'Wavy/12': ('position': top, 'size': 100% auto, 'colors': (1), 'repeat-y': false), + 'Wavy/13': ('position': bottom, 'size': 100% auto, 'colors': (4), 'repeat-y': false), + 'Wavy/14': ('position': bottom, 'size': 100% auto, 'colors': (1, 3), 'repeat-y': false), + 'Wavy/15': ('position': top, 'size': 100% auto, 'colors': (1), 'repeat-y': false), + 'Wavy/16': ('position': bottom, 'size': 100% auto, 'colors': (1), 'repeat-y': false), + 'Wavy/17': ('position': top, 'size': 100% auto, 'colors': (1), 'repeat-y': false), + 'Wavy/18': ('position': bottom, 'size': 100% auto, 'colors': (5), 'repeat-y': false), + 'Wavy/19': ('position': top, 'size': 100% auto, 'colors': (5), 'repeat-y': false), + 'Wavy/20': ('position': bottom, 'size': 100% auto, 'colors': (2), 'repeat-y': false), + 'Wavy/21': ('position': top, 'size': 100% auto, 'colors': (2), 'repeat-y': false), + 'Wavy/22': ('position': bottom, 'size': 100% auto, 'colors': (3), 'repeat-y': false), + 'Wavy/23': ('position': top, 'size': 100% auto, 'colors': (3), 'repeat-y': false), + 'Zigs/01': ('position': bottom, 'size': 100% auto, 'colors': (2), 'repeat-y': false), + 'Zigs/02': ('position': bottom, 'size': 100% auto, 'colors': (2), 'repeat-y': false), + 'Zigs/03': ('position': top, 'size': 100% auto, 'colors': (1), 'repeat-y': true), + 'Zigs/04': ('position': bottom, 'size': 100% auto, 'colors': (1), 'repeat-y': false), + 'Zigs/05': ('position': bottom, 'size': 100% auto, 'colors': (3), 'repeat-y': false), +)); diff --git a/addons/web_editor/static/src/scss/wysiwyg_snippets.scss b/addons/web_editor/static/src/scss/wysiwyg_snippets.scss index 7c7e7814f26..aa925a19cb7 100644 --- a/addons/web_editor/static/src/scss/wysiwyg_snippets.scss +++ b/addons/web_editor/static/src/scss/wysiwyg_snippets.scss @@ -506,10 +506,13 @@ body.editor_enable.editor_has_snippets { we-selection-items { @include o-position-absolute(100%, 0, auto, 0); z-index: $zindex-dropdown; - max-height: 600px; - overflow-y: auto; margin-top: $o-we-sidebar-content-field-dropdown-spacing !important; + &:not(.o_we_has_pager) { + max-height: 600px; + overflow-y: auto; + } + > we-title { line-height: $o-we-sidebar-content-field-dropdown-item-height; } @@ -541,6 +544,25 @@ body.editor_enable.editor_has_snippets { } } } + .o_we_pager_controls { + display: flex; + align-items: center; + padding: $o-we-sidebar-content-field-label-spacing; + } + .o_we_pager_next, .o_we_pager_prev { + padding: $o-we-sidebar-content-field-label-spacing; + cursor: pointer; + } + we-select-page { + display: none; + width: 100%; + max-height: 600px; + overflow-y: auto; + + &.active { + display: block; + } + } } // Button groups @@ -1437,3 +1459,21 @@ body.editor_enable.editor_has_snippets { .o_container_preview { outline: 2px dashed $o-we-handles-accent-color; } + +we-select.o_we_shape_menu { + we-button[data-shape] { + padding: 0 !important; + + &.active { + border: 1px solid $o-we-color-success !important; + } + div { + width: 100%; + } + .o_we_shape_btn_content { + width: 100%; + height: 75px; + background-color: lightgray; // Must be a color which can make all shapes appear + } + } +} |
