diff options
Diffstat (limited to 'addons/web_editor/static/src')
8 files changed, 398 insertions, 25 deletions
diff --git a/addons/web_editor/static/src/img/curved_arrow.svg b/addons/web_editor/static/src/img/curved_arrow.svg new file mode 100644 index 00000000000..a8ed9acd242 --- /dev/null +++ b/addons/web_editor/static/src/img/curved_arrow.svg @@ -0,0 +1,14 @@ +<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1144 1280"> +<g transform="translate(0,1280) scale(0.1,-0.1)" fill="#000000" stroke="none"> +<path d="M7166 11004 c-603 -901 -1094 -1639 -1092 -1641 1 -2 369 42 817 97 +448 55 825 101 838 103 30 3 26 14 86 -278 131 -634 218 -1317 255 -2010 13 +-246 13 -898 0 -1135 -71 -1262 -367 -2292 -883 -3065 -162 -242 -308 -418 +-541 -650 -661 -658 -1465 -1094 -2581 -1400 -1069 -293 -2283 -471 -3660 +-536 -132 -6 -242 -13 -244 -15 -6 -5 16 -190 30 -255 9 -43 13 -47 47 -53 +108 -17 1148 -2 1627 24 2760 152 4778 866 6094 2155 506 496 887 1018 1216 +1670 444 877 715 1860 819 2970 35 365 41 510 41 1000 0 581 -17 880 -76 1355 +-19 158 -65 452 -74 477 -5 14 -54 7 730 104 363 44 662 83 665 85 2 3 0 8 -6 +12 -6 4 -682 594 -1503 1312 -822 718 -1497 1306 -1501 1308 -4 2 -501 -734 +-1104 -1634z"/> +</g> +</svg> diff --git a/addons/web_editor/static/src/js/common/utils.js b/addons/web_editor/static/src/js/common/utils.js index c66bb75a47c..6e92bd6955b 100644 --- a/addons/web_editor/static/src/js/common/utils.js +++ b/addons/web_editor/static/src/js/common/utils.js @@ -32,6 +32,18 @@ const CSS_UNITS_CONVERSION = { 'rem-px': () => _computePxByRem(), 'px-rem': () => _computePxByRem(true), }; +/** + * Colors of the default palette, used for substitution in shapes/illustrations. + * key: number of the color in the palette (ie, o-color-<1-5>) + * value: color hex code + */ +const DEFAULT_PALETTE = { + '1': '#3AADAA', + '2': '#7C6576', + '3': '#F6F6F6', + '4': '#FFFFFF', + '5': '#383E45', +}; /** * Computes the number of "px" needed to make a "rem" unit. Subsequent calls @@ -218,6 +230,7 @@ function _getBgImageURL(el) { return { CSS_SHORTHANDS: CSS_SHORTHANDS, CSS_UNITS_CONVERSION: CSS_UNITS_CONVERSION, + DEFAULT_PALETTE: DEFAULT_PALETTE, computePxByRem: _computePxByRem, convertValueToUnit: _convertValueToUnit, convertNumericToUnit: _convertNumericToUnit, diff --git a/addons/web_editor/static/src/js/editor/snippets.editor.js b/addons/web_editor/static/src/js/editor/snippets.editor.js index 4254afe270f..37eda9910d1 100644 --- a/addons/web_editor/static/src/js/editor/snippets.editor.js +++ b/addons/web_editor/static/src/js/editor/snippets.editor.js @@ -10,6 +10,7 @@ var options = require('web_editor.snippets.options'); var Wysiwyg = require('web_editor.wysiwyg'); const {ColorPaletteWidget} = require('web_editor.ColorPalette'); const SmoothScrollOnDrag = require('web/static/src/js/core/smooth_scroll_on_drag.js'); +const {getCSSVariableValue} = require('web_editor.utils'); var _t = core._t; @@ -1853,6 +1854,12 @@ var SnippetsMenu = Widget.extend({ } $toInsert = $baseBody.clone(); + // Color-customize dynamic SVGs in dropped snippets with current theme colors. + [...$toInsert.find('img[src^="/web_editor/shape/"]')].forEach(dynamicSvg => { + const colorCustomizedURL = new URL(dynamicSvg.getAttribute('src'), window.location.origin); + colorCustomizedURL.searchParams.set('c1', getCSSVariableValue('o-color-1')); + dynamicSvg.src = colorCustomizedURL.pathname + colorCustomizedURL.search; + }); if (!$selectorSiblings.length && !$selectorChildren.length) { console.warn($snippet.find('.oe_snippet_thumbnail_title').text() + " have not insert action: data-drop-near or data-drop-in"); 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 8e236bb93d1..1a0e8d34935 100644 --- a/addons/web_editor/static/src/js/editor/snippets.options.js +++ b/addons/web_editor/static/src/js/editor/snippets.options.js @@ -3578,6 +3578,30 @@ registry.BackgroundImage = SnippetOptionWidget.extend({ this.$target.trigger('background_changed', [previewMode]); } }, + /** + * Changes the main color of dynamic SVGs. + * + * @see this.selectClass for parameters + */ + async dynamicColor(previewMode, widgetValue, params) { + const currentSrc = getBgImageURL(this.$target[0]); + switch (previewMode) { + case true: + this.previousSrc = currentSrc; + break; + case 'reset': + this.$target.css('background-image', `url('${this.previousSrc}')`); + return; + } + const newURL = new URL(currentSrc, window.location.origin); + newURL.searchParams.set('c1', normalizeColor(widgetValue)); + const src = newURL.pathname + newURL.search; + await loadImage(src); + this.$target.css('background-image', `url('${src}')`); + if (!previewMode) { + this.previousSrc = src; + } + }, //-------------------------------------------------------------------------- // Public @@ -3608,8 +3632,21 @@ registry.BackgroundImage = SnippetOptionWidget.extend({ * @override */ _computeWidgetState: function (methodName) { - if (methodName === 'background') { - return getBgImageURL(this.$target[0]); + switch (methodName) { + case 'background': + return getBgImageURL(this.$target[0]); + case 'dynamicColor': + return new URL(getBgImageURL(this.$target[0]), window.location.origin).searchParams.get('c1'); + } + return this._super(...arguments); + }, + /** + * @override + */ + _computeWidgetVisibility(widgetName, params) { + if (widgetName === 'dynamic_color_opt') { + const src = new URL(getBgImageURL(this.$target[0]), window.location.origin); + return src.origin === window.location.origin && src.pathname.startsWith('/web_editor/shape/'); } return this._super(...arguments); }, @@ -4505,6 +4542,86 @@ registry.SnippetSave = SnippetOptionWidget.extend({ }, }); +/** + * Handles the dynamic colors for dynamic SVGs. + */ +registry.DynamicSvg = SnippetOptionWidget.extend({ + /** + * @override + */ + start() { + this.$target.on('image_changed.DynamicSvg', this._onImageChanged.bind(this)); + return this._super(...arguments); + }, + /** + * @override + */ + destroy() { + this.$target.off('.DynamicSvg'); + return this._super(...arguments); + }, + + //-------------------------------------------------------------------------- + // Options + //-------------------------------------------------------------------------- + + /** + * Sets the dynamic SVG's dynamic color. + * + * @see this.selectClass for params + */ + async color(previewMode, widgetValue, params) { + const target = this.$target[0]; + switch (previewMode) { + case true: + this.previousSrc = target.getAttribute('src'); + break; + case 'reset': + target.src = this.previousSrc; + return; + } + const newURL = new URL(target.src, window.location.origin); + newURL.searchParams.set('c1', normalizeColor(widgetValue)); + const src = newURL.pathname + newURL.search; + await loadImage(src); + target.src = src; + if (!previewMode) { + this.previousSrc = src; + } + }, + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * @override + */ + _computeWidgetState(methodName, params) { + switch (methodName) { + case 'color': + return new URL(this.$target[0].src, window.location.origin).searchParams.get('c1'); + } + return this._super(...arguments); + }, + /** + * @override + */ + _computeVisibility(methodName, params) { + return this.$target.is("img[src^='/web_editor/shape/']"); + }, + + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + + /** + * @override + */ + _onImageChanged(methodName, params) { + return this.updateUI(); + }, +}); return { SnippetOptionWidget: SnippetOptionWidget, diff --git a/addons/web_editor/static/src/js/wysiwyg/widgets/media.js b/addons/web_editor/static/src/js/wysiwyg/widgets/media.js index d405d647ce7..3134f55b373 100644 --- a/addons/web_editor/static/src/js/wysiwyg/widgets/media.js +++ b/addons/web_editor/static/src/js/wysiwyg/widgets/media.js @@ -10,6 +10,7 @@ var utils = require('web.utils'); var Widget = require('web.Widget'); var session = require('web.session'); const {removeOnImageChangeAttrs} = require('web_editor.image_processing'); +const {getCSSVariableValue, DEFAULT_PALETTE} = require('web_editor.utils'); var QWeb = core.qweb; var _t = core._t; @@ -106,6 +107,7 @@ var SearchableMediaWidget = MediaWidget.extend({ * @private */ _onSearchInput: function (ev) { + this.attachments = []; this.search($(ev.currentTarget).val() || '').then(() => this._renderThumbnails()); this.hasSearched = true; }, @@ -128,6 +130,7 @@ var FileWidget = SearchableMediaWidget.extend({ IMAGE_MIMETYPES: ['image/gif', 'image/jpe', 'image/jpeg', 'image/jpg', 'image/gif', 'image/png', 'image/svg+xml'], NUMBER_OF_ATTACHMENTS_TO_DISPLAY: 30, + MAX_DB_ATTACHMENTS: 5, /** * @constructor @@ -144,6 +147,8 @@ var FileWidget = SearchableMediaWidget.extend({ this.attachments = []; this.selectedAttachments = []; + this.libraryMedia = []; + this.selectedMedia = []; this._onUploadURLButtonClick = dom.makeAsyncHandler(this._onUploadURLButtonClick); }, @@ -204,7 +209,6 @@ var FileWidget = SearchableMediaWidget.extend({ * @override */ search: function (needle) { - this.attachments = []; this.needle = needle; return this.fetchAttachments(this.NUMBER_OF_ATTACHMENTS_TO_DISPLAY, 0); }, @@ -231,6 +235,12 @@ var FileWidget = SearchableMediaWidget.extend({ Array.prototype.splice.apply(this.attachments, [offset, attachments.length].concat(attachments)); }); }, + /** + * Computes whether there is content to display in the template. + */ + hasContent() { + return this.attachments.length; + }, //-------------------------------------------------------------------------- // Private @@ -292,7 +302,7 @@ var FileWidget = SearchableMediaWidget.extend({ domain.push(['name', 'ilike', needle]); } domain.push('!', ['name', '=like', '%.crop']); - domain.push('|', ['type', '=', 'binary'], ['url', '!=', false]); + domain.push('|', ['type', '=', 'binary'], '!', ['url', '=like', '/%/static/%']); return domain; }, /** @@ -357,6 +367,7 @@ var FileWidget = SearchableMediaWidget.extend({ this._highlightSelected(); // adapt load more + this.$('.o_we_load_more').toggleClass('d-none', !this.hasContent()); var noLoadMoreButton = this.NUMBER_OF_ATTACHMENTS_TO_DISPLAY >= this.attachments.length; var noMoreImgToLoad = this.numberOfAttachmentsToDisplay >= this.attachments.length; this.$('.o_load_done_msg').toggleClass('d-none', noLoadMoreButton || !noMoreImgToLoad); @@ -367,11 +378,36 @@ var FileWidget = SearchableMediaWidget.extend({ * @returns {Promise} */ _save: async function () { + // Create all media-library attachments. + const toSave = Object.fromEntries(this.selectedMedia.map(media => [ + media.id, { + query: media.query || '', + is_dynamic_svg: !!media.isDynamicSVG, + } + ])); + let mediaAttachments = []; + if (Object.keys(toSave).length !== 0) { + mediaAttachments = await this._rpc({ + route: '/web_editor/save_library_media', + params: { + media: toSave, + }, + }); + } + const selected = this.selectedAttachments.concat(mediaAttachments).map(attachment => { + // Color-customize dynamic SVGs with the primary theme color + if (attachment.image_src.startsWith('/web_editor/shape/')) { + const colorCustomizedURL = new URL(attachment.image_src, window.location.origin); + colorCustomizedURL.searchParams.set('c1', getCSSVariableValue('o-color-1')); + attachment.image_src = colorCustomizedURL.pathname + colorCustomizedURL.search; + } + return attachment; + }); if (this.options.multiImages) { - return this.selectedAttachments; + return selected; } - var img = this.selectedAttachments[0]; + const img = selected[0]; if (!img || !img.id) { return this.media; } @@ -435,22 +471,30 @@ var FileWidget = SearchableMediaWidget.extend({ * and to close the media dialog * @private */ - _selectAttachement: function (attachment, save) { + _selectAttachement: function (attachment, save, {type = 'attachment'} = {}) { + const possibleProps = { + 'attachment': 'selectedAttachments', + 'media': 'selectedMedia' + }; + const prop = possibleProps[type]; if (this.options.multiImages) { // if the clicked attachment is already selected then unselect it // unless it was a save request (then keep the current selection) - var index = this.selectedAttachments.indexOf(attachment); + const index = this[prop].indexOf(attachment); if (index !== -1) { if (!save) { - this.selectedAttachments.splice(index, 1); + this[prop].splice(index, 1); } } else { // if the clicked attachment is not selected, add it to selected - this.selectedAttachments.push(attachment); + this[prop].push(attachment); } } else { + Object.values(possibleProps).forEach(prop => { + this[prop] = []; + }); // select the clicked attachment - this.selectedAttachments = [attachment]; + this[prop] = [attachment]; } this._highlightSelected(); if (save) { @@ -481,9 +525,15 @@ var FileWidget = SearchableMediaWidget.extend({ * @private */ _onAttachmentClick: function (ev) { - var $attachment = $(ev.currentTarget); - var attachment = _.find(this.attachments, {id: $attachment.data('id')}); - this._selectAttachement(attachment, !this.options.multiImages); + const attachment = ev.currentTarget; + const {id: attachmentID, mediaId} = attachment.dataset; + if (attachmentID) { + const attachment = this.attachments.find(attachment => attachment.id === parseInt(attachmentID)); + this._selectAttachement(attachment, !this.options.multiImages); + } else if (mediaId) { + const media = this.libraryMedia.find(media => media.id === parseInt(mediaId)); + this._selectAttachement(media, !this.options.multiImages, {type: 'media'}); + } }, /** * Handles change of the file input: create attachments with the new files @@ -639,6 +689,7 @@ var FileWidget = SearchableMediaWidget.extend({ * @override */ _onSearchInput: function () { + this.attachments = []; this.numberOfAttachmentsToDisplay = this.NUMBER_OF_ATTACHMENTS_TO_DISPLAY; this._super.apply(this, arguments); }, @@ -652,6 +703,7 @@ var ImageWidget = FileWidget.extend({ existingAttachmentsTemplate: 'wysiwyg.widgets.image.existing.attachments', events: Object.assign({}, FileWidget.prototype.events, { 'change input.o_we_show_optimized': '_onShowOptimizedChange', + 'change .o_we_search_select': '_onSearchSelect', }), MIN_ROW_HEIGHT: 128, @@ -659,6 +711,7 @@ var ImageWidget = FileWidget.extend({ * @constructor */ init: function (parent, media, options) { + this.searchService = 'all'; options = _.extend({ accept: 'image/*', mimetypeDomain: [['mimetype', 'in', this.IMAGE_MIMETYPES]], @@ -681,6 +734,60 @@ var ImageWidget = FileWidget.extend({ this.el.removeEventListener('load', this._onAttachmentImageLoad, true); return this._super(...arguments); }, + /** + * @override + */ + async fetchAttachments(number, offset) { + if (this.needle && this.searchService !== 'database') { + number = this.MAX_DB_ATTACHMENTS; + offset = 0; + } + const result = await this._super(number, offset); + // Color-substitution for dynamic SVG attachment + const primaryColor = getCSSVariableValue('o-color-1'); + this.attachments.forEach(attachment => { + if (attachment.image_src.startsWith('/')) { + const newURL = new URL(attachment.image_src, window.location.origin); + // Set the main color of dynamic SVGs to o-color-1 + if (attachment.image_src.startsWith('/web_editor/shape/')) { + newURL.searchParams.set('c1', primaryColor); + } else { + // Set height so that db images load faster + newURL.searchParams.set('height', 2 * this.MIN_ROW_HEIGHT); + } + attachment.thumbnail_src = newURL.pathname + newURL.search; + } + }); + if (this.needle) { + try { + const response = await this._rpc({ + route: '/web_editor/media_library_search', + params: { + 'query': this.needle, + 'offset': this.libraryMedia.length, + }, + }); + const newMedia = response.media; + this.nbMediaResults = response.results; + this.libraryMedia.push(...newMedia); + } catch (e) { + // Either API endpoint doesn't exist or is misconfigured. + console.error(`Couldn't reach API endpoint.`); + } + } + return result; + }, + /** + * @override + */ + hasContent() { + if (this.searchService === 'all') { + return this._super(...arguments) || this.libraryMedia.length; + } else if (this.searchService === 'media-library') { + return !!this.libraryMedia.length; + } + return this._super(...arguments); + }, //-------------------------------------------------------------------------- // Private @@ -708,7 +815,7 @@ var ImageWidget = FileWidget.extend({ this.$('.o_existing_attachment_cell').addClass('d-none'); // Replace images that had been previously loaded if any to prevent scroll resetting to top alreadyLoaded.each((index, el) => { - const toReplace = this.$(`.o_existing_attachment_cell[data-id="${el.dataset.id}"]`); + const toReplace = this.$(`.o_existing_attachment_cell[data-id="${el.dataset.id}"], .o_existing_attachment_cell[data-media-id="${el.dataset.mediaId}"]`); if (toReplace.length) { toReplace.replaceWith(el); } @@ -720,13 +827,23 @@ var ImageWidget = FileWidget.extend({ flexGrow: placeholderWidth, flexBasis: placeholderWidth, }); + if (this.needle && ['media-library', 'all'].includes(this.searchService)) { + const noMoreImgToLoad = this.libraryMedia.length === this.nbMediaResults; + const noLoadMoreButton = noMoreImgToLoad && this.libraryMedia.length <= 15; + this.$('.o_load_done_msg').toggleClass('d-none', noLoadMoreButton || !noMoreImgToLoad); + this.$('.o_load_more').toggleClass('d-none', noMoreImgToLoad); + } }, /** * @override */ _renderExisting: function (attachments) { + if (this.needle && this.searchService !== 'database') { + attachments = attachments.slice(0, this.MAX_DB_ATTACHMENTS); + } return QWeb.render(this.existingAttachmentsTemplate, { attachments: attachments, + libraryMedia: this.libraryMedia, widget: this, }); }, @@ -738,6 +855,16 @@ var ImageWidget = FileWidget.extend({ _toggleOptimized: function (value) { this.$('.o_we_attachment_optimized').each((i, cell) => cell.style.setProperty('display', value ? null : 'none', 'important')); }, + /** + * @override + */ + _highlightSelected: function () { + this._super(...arguments); + this.selectedMedia.forEach(media => { + this.$(`.o_existing_attachment_cell[data-media-id=${media.id}]`) + .addClass("o_we_attachment_selected"); + }); + }, //-------------------------------------------------------------------------- // Handlers @@ -746,12 +873,37 @@ var ImageWidget = FileWidget.extend({ /** * @override */ - _onAttachmentImageLoad: function (ev) { + _onAttachmentImageLoad: async function (ev) { const img = ev.target; const cell = img.closest('.o_existing_attachment_cell'); if (!cell) { return; } + if (cell.dataset.mediaId && !img.src.startsWith('blob')) { + const mediaUrl = img.src; + try { + const response = await fetch(mediaUrl); + if (response.headers.get('content-type') === 'image/svg+xml') { + const svg = await response.text(); + const colorRegex = new RegExp(DEFAULT_PALETTE['1'], 'gi'); + if (colorRegex.test(svg)) { + const fileName = mediaUrl.split('/').pop(); + const file = new File([svg.replace(colorRegex, getCSSVariableValue('o-color-1'))], fileName, { + type: "image/svg+xml", + }); + img.src = URL.createObjectURL(file); + const media = this.libraryMedia.find(media => media.id === parseInt(cell.dataset.mediaId)); + if (media) { + media.isDynamicSVG = true; + } + // We changed the src: wait for the next load event to do the styling + return; + } + } + } catch (e) { + console.error('CORS is misconfigured on the API server, image will be treated as non-dynamic.'); + } + } const aspectRatio = img.naturalWidth / img.naturalHeight; const width = aspectRatio * this.MIN_ROW_HEIGHT; cell.style.flexGrow = width; @@ -766,6 +918,21 @@ var ImageWidget = FileWidget.extend({ _onShowOptimizedChange: function (ev) { this._toggleOptimized(ev.target.checked); }, + /** + * @override + */ + _onSearchSelect: function (ev) { + const {value} = ev.target; + this.searchService = value; + this.$('.o_we_search').trigger('input'); + }, + /** + * @private + */ + _onSearchInput: function (ev) { + this.libraryMedia = []; + this._super(...arguments); + }, }); diff --git a/addons/web_editor/static/src/scss/web_editor.common.scss b/addons/web_editor/static/src/scss/web_editor.common.scss index 0cd22143290..ea7aee164e6 100644 --- a/addons/web_editor/static/src/scss/web_editor.common.scss +++ b/addons/web_editor/static/src/scss/web_editor.common.scss @@ -469,6 +469,31 @@ a.o_underline { } } +.o_we_search_prompt { + position: relative; + min-height: 250px; + width: 100%; + display: flex; + align-items: center; + justify-content: center; + + & > h2 { + max-width: 500px; + text-align: center; + } + + &::before { + content: ""; + @include o-position-absolute($top: 0, $right: 50px); + width: 100px; + height: 150px; + opacity: .5; + background-image: url('/web_editor/static/src/img/curved_arrow.svg'); + background-size: 100%; + background-repeat: no-repeat; + } +} + @include media-breakpoint-down(sm) { odoo-wysiwyg-container { .panel-heading.note-toolbar { diff --git a/addons/web_editor/static/src/scss/wysiwyg.scss b/addons/web_editor/static/src/scss/wysiwyg.scss index 6eaa65c6e56..39fa326c737 100644 --- a/addons/web_editor/static/src/scss/wysiwyg.scss +++ b/addons/web_editor/static/src/scss/wysiwyg.scss @@ -394,6 +394,15 @@ img.o_we_selected_image { img::selection { background: transparent; } +.o_we_media_author { + font-size: 11px; + @include o-position-absolute($bottom: 0, $left: 0, $right: 0); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + text-align: center; + background-color: rgba(255, 255, 255, .7); +} @include media-breakpoint-down(md) { #web_editor-top-edit { diff --git a/addons/web_editor/static/src/xml/wysiwyg.xml b/addons/web_editor/static/src/xml/wysiwyg.xml index 714d0913e5d..96823fde263 100644 --- a/addons/web_editor/static/src/xml/wysiwyg.xml +++ b/addons/web_editor/static/src/xml/wysiwyg.xml @@ -125,7 +125,7 @@ <t t-call="wysiwyg.widgets.files.submenu"/> <div class="form-text o_we_error_text"/> <div class="o_we_existing_attachments"/> - <div class="mt-4 text-center mx-auto"> + <div class="mt-4 text-center mx-auto o_we_load_more"> <button class="btn btn-primary o_load_more d-none" type="button">Load more...</button> <div class="mt-4 o_load_done_msg d-none"> <span><i>All images have been loaded</i></span> @@ -170,12 +170,17 @@ <t t-set="searchPlaceholder">Search an image</t> <t t-set="urlWarningTitle">The URL does not contain any image. The file will be added in the document section.</t> <div class="d-flex justify-content-end flex-grow-1 pr-3"> - <div t-attf-class="custom-control custom-switch #{__debug__ or 'd-none'}"> + <div t-attf-class="custom-control custom-switch #{__debug__ and 'd-flex' or 'd-none'} align-items-center mr-2"> <input class="o_we_show_optimized ml-2 custom-control-input" type="checkbox" id="o_we_show_optimized_switch"/> <label class="custom-control-label" for="o_we_show_optimized_switch"> Show optimized images </label> </div> + <select class="custom-select o_we_search_select"> + <option value="all">All</option> + <option value="database">My Images</option> + <option value="media-library">Illustrations</option> + </select> </div> </t> </t> @@ -288,14 +293,22 @@ <t t-name="wysiwyg.widgets.image.existing.attachments"> <div class="o_we_existing_attachments o_we_images d-flex flex-wrap w-100 justify-content-between align-items-stretch my-0"> - <div t-if="!attachments.length" class="o_nocontent_help"> - <p class="o_empty_folder_image">No images found.</p> - <p class="o_empty_folder_subtitle">You can upload images with the button located in the top left of the screen.</p> - </div> + <t t-if="!widget.hasContent()"> + <div t-if="widget.needle" class="o_nocontent_help"> + <p class="o_empty_folder_image">No images found.</p> + <p class="o_empty_folder_subtitle">You can upload images with the button located in the top left of the screen.</p> + </div> + <div t-else="" class="o_we_search_prompt"> + <h2>Get the perfect image by searching in our library of copyright free photos and illustrations.</h2> + </div> + </t> <t t-else=""> - <t t-foreach="attachments" t-as="attachment"> + <t t-if="['all', 'database'].includes(widget.searchService)" t-foreach="attachments" t-as="attachment"> <t t-call="wysiwyg.widgets.image.existing.attachment"/> </t> + <t t-if="['all', 'media-library'].includes(widget.searchService)" t-foreach="libraryMedia" t-as="media"> + <t t-call="wysiwyg.widgets.image.library_media"/> + </t> <!-- 20 placeholders is just enough for a 5K screen, change this if ImageWidget.MIN_ROW_HEIGHT changes --> <t t-foreach="20"> <div class="o_we_attachment_placeholder"/> @@ -309,13 +322,21 @@ <div t-attf-class="o_existing_attachment_cell position-relative bg-light #{isOptimized and 'o_we_attachment_optimized d-none' or ''} align-items-center justify-content-center" t-att-data-id="attachment.id"> <t t-call="wysiwyg.widgets.file.existing.remove"/> <div class="o_we_media_dialog_img_wrapper"> - <!-- 256 is 2 * ImageWidget.MIN_ROW_HEIGHT, hopefully the highest display width of any image --> - <img class="img img-fluid o_we_attachment_highlight" t-attf-src="#{attachment.image_src}?height=256" t-att-alt="attachment.name" t-att-title="attachment.name"/> + <img class="img img-fluid o_we_attachment_highlight" t-attf-src="#{attachment.thumbnail_src or attachment.image_src}" t-att-alt="attachment.name" t-att-title="attachment.name"/> </div> <span t-if="isOptimized" class="badge badge-success">Optimized</span> </div> </t> + <t t-name="wysiwyg.widgets.image.library_media"> + <div t-attf-class="o_existing_attachment_cell position-relative bg-light align-items-center justify-content-center o_library_media_cell" t-att-data-media-id="media.id"> + <div class="o_we_media_dialog_img_wrapper"> + <img class="img img-fluid o_we_attachment_highlight" t-attf-src="#{media.thumbnail_url}" t-att-title="media.tooltip or ''" crossorigin="anonymous"/> + <a t-if="media.author" class="o_we_media_author" t-att-href="media.author_link" target="_blank" t-esc="media.author"/> + </div> + </div> + </t> + <t t-name="wysiwyg.widgets.document.existing.attachments"> <div class="o_we_existing_attachments o_we_documents"> <div t-if="!attachments.length" class="o_nocontent_help"> |
