From 8ca115b3b9dfe87b59b6b064a4d85ea152d9214c Mon Sep 17 00:00:00 2001 From: Antoine Guenet Date: Tue, 25 Apr 2023 10:59:35 +0000 Subject: [PATCH] [ADD] web_editor: full screen image preview on dblclick (html field) When double-clicking on an image in an html field, display it "full screen" (similar to clicking on an image in the attachments list). This replaces the current behavior (which is to open the media selector to replace the current image - the Replace action remains available in the toolbar). In the toolbar for images, display an icon that users can click on to open the full screen preview as well. task-3109331 closes odoo/odoo#119625 Related: odoo/enterprise#40450 Signed-off-by: David Monjoie (dmo) --- .../static/src/js/backend/html_field.js | 7 ++ .../static/src/js/wysiwyg/wysiwyg.js | 84 ++++++++++++++----- addons/web_editor/static/src/xml/editor.xml | 4 + 3 files changed, 73 insertions(+), 22 deletions(-) diff --git a/addons/web_editor/static/src/js/backend/html_field.js b/addons/web_editor/static/src/js/backend/html_field.js index 3aee22db4a7..c2ae82d701f 100644 --- a/addons/web_editor/static/src/js/backend/html_field.js +++ b/addons/web_editor/static/src/js/backend/html_field.js @@ -235,6 +235,7 @@ export class HtmlField extends Component { value: this.props.record.data[this.props.name], autostart: false, onAttachmentChange: this._onAttachmentChange.bind(this), + onDblClickEditableMedia: this._onDblClickEditableMedia.bind(this), onWysiwygBlur: this._onWysiwygBlur.bind(this), ...wysiwygOptions, ...dynamicPlaceholderOptions, @@ -570,6 +571,12 @@ export class HtmlField extends Component { }, }); } + _onDblClickEditableMedia(ev) { + const el = ev.target; + if (el.nodeName === 'IMG' && el.src) { + this.wysiwyg.showImageFullscreen(el.src); + } + } _onWysiwygBlur() { this.commitChanges(); } diff --git a/addons/web_editor/static/src/js/wysiwyg/wysiwyg.js b/addons/web_editor/static/src/js/wysiwyg/wysiwyg.js index 8bd269bad39..3480b0c3e1e 100644 --- a/addons/web_editor/static/src/js/wysiwyg/wysiwyg.js +++ b/addons/web_editor/static/src/js/wysiwyg/wysiwyg.js @@ -25,6 +25,8 @@ import { PeerToPeer } from "@web_editor/js/wysiwyg/PeerToPeer"; import { uniqueId } from "@web/core/utils/functions"; import { groupBy } from "@web/core/utils/arrays"; import { debounce } from "@web/core/utils/timing"; +import { registry } from "@web/core/registry"; +import { FileViewer } from "@web/core/file_viewer/file_viewer"; var _t = core._t; const QWeb = core.qweb; @@ -68,6 +70,8 @@ const PTP_CLIENT_DISCONNECTED_STATES = [ // this is a local cache for ice server descriptions let ICE_SERVERS = null; +let fileViewerId = 0; + const Wysiwyg = Widget.extend({ defaultOptions: { lang: 'odoo', @@ -306,29 +310,17 @@ const Wysiwyg = Widget.extend({ this.$editable.on('click', '.o_image, .media_iframe_video', e => e.preventDefault()); this.showTooltip = true; - this.$editable.on('dblclick', mediaSelector, function () { - if (this.isContentEditable || (this.parentElement && this.parentElement.isContentEditable)) { - self.showTooltip = false; + this.$editable.on('dblclick', mediaSelector, ev => { + const target = ev.target; + if (target.isContentEditable || (target.parentElement && target.parentElement.isContentEditable)) { + this.showTooltip = false; - const selection = self.odooEditor.document.getSelection(); - const anchorNode = selection.anchorNode; - if (isProtected(anchorNode)) { - return; - } - - const $el = $(this); - let params = {node: this}; - $el.selectElement(); - - if (!$el.parent().hasClass('o_stars')) { - // Waiting for all the options to be initialized before - // opening the media dialog and only if the media has not - // been deleted in the meantime. - self.waitForEmptyMutexAction().then(() => { - if ($el[0].parentElement) { - self.openMediaDialog(params); - } - }); + if (!isProtected(this.odooEditor.document.getSelection().anchorNode)) { + if (this.options.onDblClickEditableMedia && target.nodeName === 'IMG' && target.src) { + this.options.onDblClickEditableMedia(ev); + } else { + this._onDblClickEditableMedia(ev); + } } } }); @@ -1365,6 +1357,33 @@ const Wysiwyg = Widget.extend({ this.linkTools = undefined; } }, + /** + * Take an image's URL and display it in a fullscreen viewer. + * + * @todo should use `useFileViewer` instead once Wysiwyg becomes an Owl Component. + * @param {string} url + */ + showImageFullscreen(url) { + const viewerId = `web.file_viewer${fileViewerId++}`; + registry.category("main_components").add(viewerId, { + Component: FileViewer, + props: { + files: [{ + isImage: true, + isViewable: true, + displayName: url, + defaultSource: url, + downloadUrl: url, + }], + startIndex: 0, + close: () => { + registry.category('main_components').remove(viewerId); + }, + }, + }); + this.odooEditor.document.getSelection()?.removeAllRanges(); + this.odooEditor.editable.blur(); + }, /** * Open the media dialog. * @@ -1535,6 +1554,12 @@ const Wysiwyg = Widget.extend({ if (!options.snippets) { $toolbar.find('#justify, #media-insert').remove(); } + $toolbar.find('#image-fullscreen').click(() => { + if (!this.lastMediaClicked?.src) { + return; + } + this.showImageFullscreen(this.lastMediaClicked.src); + }) $toolbar.find('#media-insert, #media-replace, #media-description').click(openTools); $toolbar.find('#create-link').click(openTools); $toolbar.find('#image-shape div, #fa-spin').click(e => { @@ -1877,6 +1902,7 @@ const Wysiwyg = Widget.extend({ const isInMedia = $target.is(mediaSelector) && !$target.parent().hasClass('o_stars') && e.target && (e.target.isContentEditable || (e.target.parentElement && e.target.parentElement.isContentEditable)); this.toolbar.$el.find([ + '#image-preview', '#image-shape', '#image-width', '#image-padding', @@ -2471,6 +2497,20 @@ const Wysiwyg = Widget.extend({ this.options.onAttachmentChange(attachment); } }, + _onDblClickEditableMedia(ev) { + const $el = $(ev.target); + $el.selectElement(); + if (!$el.parent().hasClass('o_stars')) { + // Waiting for all the options to be initialized before + // opening the media dialog and only if the media has not + // been deleted in the meantime. + this.waitForEmptyMutexAction().then(() => { + if ($el[0].parentElement) { + this.openMediaDialog({ node: ev.target }); + } + }); + } + }, _onSelectionChange() { if (this.odooEditor.autohideToolbar) { const isVisible = this.linkPopover && this.linkPopover.el.offsetParent; diff --git a/addons/web_editor/static/src/xml/editor.xml b/addons/web_editor/static/src/xml/editor.xml index c0fedb3225f..351528e9df0 100644 --- a/addons/web_editor/static/src/xml/editor.xml +++ b/addons/web_editor/static/src/xml/editor.xml @@ -146,6 +146,10 @@ +
+
+
+