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 4baf10f565b..44b10c38fc9 100644
--- a/addons/web_editor/static/src/js/editor/snippets.editor.js
+++ b/addons/web_editor/static/src/js/editor/snippets.editor.js
@@ -3010,7 +3010,9 @@ var SnippetsMenu = Widget.extend({
const range = selection && selection.rangeCount && selection.getRangeAt(0);
if (!range ||
!$(range.commonAncestorContainer).parents('#wrapwrap, .iframe-editor-wrapper .o_editable').length ||
- $(range.commonAncestorContainer).parent('[data-oe-model]:not([data-oe-type="html"]):not([data-oe-field="arch"])').length
+ $(range.commonAncestorContainer).parent('[data-oe-model]:not([data-oe-type="html"]):not([data-oe-field="arch"])').length ||
+ (e && $(e.target).closest('.fa, img').length ||
+ this.options.wysiwyg.lastMediaClicked && $(this.options.wysiwyg.lastMediaClicked).is('.fa, img'))
) {
$toolbarContainer.hide();
} else {
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 5eb0c759c01..373d95f02db 100644
--- a/addons/web_editor/static/src/js/editor/snippets.options.js
+++ b/addons/web_editor/static/src/js/editor/snippets.options.js
@@ -4154,6 +4154,203 @@ registry.ReplaceMedia = SnippetOptionWidget.extend({
}
});
+/**
+ * General options that are common to a font awesome icon and an image.
+ */
+registry.StaticMediaTools = SnippetOptionWidget.extend({
+ currentShapes: {},
+ alignments: {
+ left: ['float-left'],
+ center: ['d-block', 'mx-auto'],
+ right: ['float-right'],
+ },
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ toggleShapeRounded(previewMode) {
+ this._toggleShape(previewMode, 'rounded');
+ },
+ toggleShapeCircle(previewMode) {
+ this._toggleShape(previewMode, 'rounded-circle');
+ },
+ toggleShapeShadow(previewMode) {
+ this._toggleShape(previewMode, 'shadow');
+ },
+ toggleShapeThumbnail(previewMode) {
+ this._toggleShape(previewMode, 'img-thumbnail');
+ },
+ setPadding(previewMode, padding, params) {
+ if (previewMode === true) {
+ this.currentPadding = params.possibleValues.find(c => this.$target.hasClass(c)) || '';
+ }
+ for (const value of params.possibleValues) {
+ if (previewMode === 'reset') {
+ this.$target.toggleClass(value, value === this.currentPadding);
+ } else {
+ this.$target.toggleClass(value, value === padding)
+ }
+ }
+ if (previewMode === false) {
+ this.currentPadding = padding;
+ }
+ },
+ align(previewMode, alignment, params) {
+ if (previewMode === true) {
+ this.currentAlignment = this._getAlignment();
+ }
+ const targetAlignment = previewMode === 'reset' ? this.currentAlignment : alignment;
+ for (const value of params.possibleValues) {
+ this.$target.toggleClass(this.alignments[value] || '', value === targetAlignment);
+ }
+ if (previewMode === false) {
+ this.currentAlignment = alignment;
+ }
+ },
+
+ //--------------------------------------------------------------------------
+ // Private
+ //--------------------------------------------------------------------------
+
+ /**
+ * @override
+ */
+ _computeWidgetState(methodName, params) {
+ if (['toggleShapeRounded', 'toggleShapeCircle', 'toggleShapeShadow', 'toggleShapeThumbnail'].includes(methodName)) {
+ return params.possibleValues.find(c => this.$target.hasClass(c)) || '';
+ } else if (methodName === 'setPadding') {
+ return params.possibleValues.find(c => this.$target.hasClass(c)) || '';
+ } else if (methodName === 'align') {
+ return this._getAlignment();
+ }
+ return this._super(...arguments);
+ },
+ _getAlignment() {
+ for (const [alignment, [alignClass]] of Object.entries(this.alignments)) {
+ if (this.$target.hasClass(alignClass)) {
+ return alignment;
+ }
+ }
+ return '';
+ },
+ _toggleShape(previewMode, shape) {
+ if (previewMode === true) {
+ this.currentShapes[shape] = this.$target.hasClass(shape);
+ this.$target.toggleClass(shape, true);
+ } else if (previewMode === 'reset') {
+ this.$target.toggleClass(shape, this.currentShapes[shape]);
+ } else if (previewMode === false) {
+ this.$target.toggleClass(shape, !this.currentShapes[shape]);
+ this.currentShapes[shape] = this.$target.hasClass(shape);
+ }
+ },
+});
+
+/**
+ * General options of a font awesome icon.
+ */
+registry.FontawesomeTools = SnippetOptionWidget.extend({
+ setSize(previewMode, size, params) {
+ if (previewMode === true) {
+ this.currentSize = params.possibleValues.find(c => this.$target.hasClass(c)) || '';
+ }
+ for (const value of params.possibleValues) {
+ if (previewMode === 'reset') {
+ this.$target.toggleClass(value, value === this.currentSize);
+ } else {
+ this.$target.toggleClass(value, value === size)
+ }
+ }
+ if (previewMode === false) {
+ this.currentSize = size;
+ }
+ },
+
+ //--------------------------------------------------------------------------
+ // Private
+ //--------------------------------------------------------------------------
+
+ /**
+ * @override
+ */
+ _computeWidgetState(methodName, params) {
+ if (methodName === 'setSize') {
+ return params.possibleValues.find(c => this.$target.hasClass(c)) || '';
+ } else if (methodName === 'align') {
+ return this._getAlignment();
+ }
+ return this._super(...arguments);
+ },
+});
+
+/**
+ * General options of an image.
+ */
+registry.ImageTools = SnippetOptionWidget.extend({
+ setWidth(previewMode, width, params) {
+ if (previewMode === true) {
+ this.currentWidth = this.$target.css('width');
+ }
+ if (previewMode === 'reset') {
+ this.$target.css('width', this.currentWidth);
+ } else {
+ this.$target.css('width', width);
+ }
+ if (previewMode === false) {
+ this.currentWidth = width;
+ }
+ },
+ setAlt(previewMode, alt) {
+ this.$target.attr('alt', alt);
+ },
+ setTitle(previewMode, title) {
+ this.$target.attr('title', title);
+ },
+ crop() {
+ new weWidgets.ImageCropWidget(this, this.$target[0]).appendTo(this.options.wysiwyg.$editable);
+ },
+ transform() {
+ if (this.$target.data('transfo-destroy')) {
+ this.$target.removeData('transfo-destroy');
+ return;
+ }
+ const document = this.$target[0].ownerDocument
+ this.$target.transfo({ document });
+ const mousedown = mousedownEvent => {
+ if (!$(mousedownEvent.target).closest('.transfo-container').length) {
+ this.$target.transfo('destroy');
+ $(document).off('mousedown', mousedown);
+ }
+ if ($(mousedownEvent.target).closest('#image-transform').length) {
+ this.$target
+ .data('transfo-destroy', true)
+ .attr('style', (this.$target.attr('style') || '')
+ .replace(/[^;]*transform[\w:]*;?/g, ''));
+ }
+ this.$target.trigger('content_changed');
+ };
+ $(document).on('mousedown', mousedown);
+ },
+
+ //--------------------------------------------------------------------------
+ // Private
+ //--------------------------------------------------------------------------
+
+ /**
+ * @override
+ */
+ _computeWidgetState(methodName, params) {
+ if (methodName === 'setWidth') {
+ return this.$target[0].style.width || '';
+ } else if (methodName === 'setAlt') {
+ return this.$target.attr('alt');
+ } else if (methodName === 'setTitle') {
+ return this.$target.attr('title');
+ }
+ return this._super(...arguments);
+ },
+});
+
/*
* Abstract option to be extended by the ImageOptimize and BackgroundOptimize
* options that handles all the common parts.
diff --git a/addons/web_editor/static/src/js/wysiwyg/widgets/alt_tools.js b/addons/web_editor/static/src/js/wysiwyg/widgets/alt_tools.js
deleted file mode 100644
index ae63e1206f3..00000000000
--- a/addons/web_editor/static/src/js/wysiwyg/widgets/alt_tools.js
+++ /dev/null
@@ -1,68 +0,0 @@
-odoo.define('wysiwyg.widgets.AltTools', function (require) {
-'use strict';
-
-var core = require('web.core');
-var Widget = require('web.Widget');
-
-var _t = core._t;
-
-/**
- * Let users change the alt & title of a media.
- */
-const AltTools = Widget.extend({
- template: 'wysiwyg.widgets.altTools',
- xmlDependencies: ['/web_editor/static/src/xml/wysiwyg.xml'],
-
- /**
- * @constructor
- */
- init: function (parent, options, media, $button) {
- options = options || {};
- this._super(parent, _.extend({}, {
- title: _t("Change media description and tooltip")
- }, options));
-
- this.$button = $button;
- this.media = media;
- const allEscQuots = /"/g;
- this.alt = ($(this.media).attr('alt') || "").replace(allEscQuots, '"');
- const title = $(this.media).attr('title') || $(this.media).data('original-title') || "";
- this.tag_title = (title).replace(allEscQuots, '"');
- },
- /**
- * @override
- */
- start: async function () {
- const def = await this._super.apply(this, arguments);
- this.$button.addClass('active');
- this.$el.find('#alt').on('input', this._onChange.bind(this));
- this.$el.find('#title').on('input', this._onChange.bind(this));
- return def;
- },
- /**
- * @override
- */
- destroy: function () {
- this.$button.removeClass('active');
- this._super(...arguments);
- },
-
- //--------------------------------------------------------------------------
- // Handlers
- //--------------------------------------------------------------------------
-
- _onChange: function (ev) {
- const attributeName = ev.target.id;
- const $target = $(ev.target);
- const $media = $(this.media);
- const allNonEscQuots = /"/g;
- const value = $target.val();
- $media.attr(attributeName, value ? value.replace(allNonEscQuots, """) : null);
- $media.trigger('content_changed');
- this.final_data = this.media;
- },
-});
-
-
-return AltTools;
-});
diff --git a/addons/web_editor/static/src/js/wysiwyg/widgets/widgets.js b/addons/web_editor/static/src/js/wysiwyg/widgets/widgets.js
index 5da9d35ad87..e70fe9ec9fb 100644
--- a/addons/web_editor/static/src/js/wysiwyg/widgets/widgets.js
+++ b/addons/web_editor/static/src/js/wysiwyg/widgets/widgets.js
@@ -3,7 +3,6 @@ odoo.define('wysiwyg.widgets', function (require) {
var Dialog = require('wysiwyg.widgets.Dialog');
var AltDialog = require('wysiwyg.widgets.AltDialog');
-var AltTools = require('wysiwyg.widgets.AltTools');
var MediaDialog = require('wysiwyg.widgets.MediaDialog');
var LinkDialog = require('wysiwyg.widgets.LinkDialog');
var LinkTools = require('wysiwyg.widgets.LinkTools');
@@ -16,7 +15,6 @@ var media = require('wysiwyg.widgets.media');
return {
Dialog: Dialog,
AltDialog: AltDialog,
- AltTools: AltTools,
MediaDialog: MediaDialog,
LinkDialog: LinkDialog,
LinkTools: LinkTools,
diff --git a/addons/web_editor/static/src/js/wysiwyg/wysiwyg.js b/addons/web_editor/static/src/js/wysiwyg/wysiwyg.js
index b240e146550..cab7d0abf85 100644
--- a/addons/web_editor/static/src/js/wysiwyg/wysiwyg.js
+++ b/addons/web_editor/static/src/js/wysiwyg/wysiwyg.js
@@ -523,28 +523,6 @@ const Wysiwyg = Widget.extend({
observerData.observer.disconnect();
}
},
- /**
- * Toggle the Alt tools in the toolbar to edit alt and title attributes.
- *
- * @param {object} params
- * @param {Node} [params.node]
- */
- toggleAltTools(params) {
- const image = (params && params.node) || this.lastMediaClicked;
- if (this.snippetsMenu) {
- if (this.altTools) {
- this.altTools.destroy();
- this.altTools = undefined;
- } else {
- const $btn = this.toolbar.$el.find('#media-description');
- this.altTools = new weWidgets.AltTools(this, params, image, $btn);
- this.altTools.appendTo(this.toolbar.$el);
- }
- } else {
- const altDialog = new weWidgets.AltDialog(this, params, image);
- altDialog.open();
- }
- },
/**
* Toggle the Link tools/dialog to edit links. If a snippet menu is present,
* use the link tools, otherwise use the dialog.
@@ -686,7 +664,7 @@ const Wysiwyg = Widget.extend({
this.openMediaDialog();
break;
case 'media-description':
- this.toggleAltTools();
+ new weWidgets.AltDialog(this, {}, this.lastMediaClicked).open();
break;
}
};
@@ -952,9 +930,6 @@ const Wysiwyg = Widget.extend({
const $target = e ? editorWindow.$(e.target) : editorWindow.$();
// Restore paragraph dropdown button's default ID.
this.toolbar.$el.find('#mediaParagraphDropdownButton').attr('id', 'paragraphDropdownButton');
- // Remove the alt tools.
- this.altTools && this.altTools.destroy();
- this.altTools = undefined;
// Hide the create-link button if the selection spans several blocks.
const selection = this.odooEditor.document.getSelection();
const range = selection.rangeCount && selection.getRangeAt(0);
diff --git a/addons/web_editor/static/src/xml/wysiwyg.xml b/addons/web_editor/static/src/xml/wysiwyg.xml
index 49090d179c3..8529c052e10 100644
--- a/addons/web_editor/static/src/xml/wysiwyg.xml
+++ b/addons/web_editor/static/src/xml/wysiwyg.xml
@@ -79,34 +79,6 @@
-
-