[IMP] web_editor: move icon and image tools to snippet option

Wherever there is a sidebar, this moves all icon and image options to
their own section of it. This also enables previews.
The "spin" option was deemed unnecessary and therefore removed.

TODO: restore the link option for icons. It wasn't working properly
before this and was removed until it is fixed so as not to do any
unnecessary coding for a broken option.

closes odoo/odoo#70952

Signed-off-by: David Monjoie (dmo) <dmo@odoo.com>
This commit is contained in:
Antoine Guenet
2021-05-21 09:00:58 +00:00
parent 2e527b4cac
commit d934b81aaa
7 changed files with 269 additions and 127 deletions
@@ -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 {
@@ -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.
@@ -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 = /&quot;/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, "&quot;") : null);
$media.trigger('content_changed');
this.final_data = this.media;
},
});
return AltTools;
});
@@ -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,
@@ -523,28 +523,6 @@ const Wysiwyg = Widget.extend({
observerData.observer.disconnect();
}
},
/**
* Toggle the Alt tools in the toolbar to edit <img> 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);
@@ -79,34 +79,6 @@
</div>
</form>
<!-- Alt Tools (allows to change alt and title of page images) -->
<t t-name="wysiwyg.widgets.altTools">
<we-customizeblock-option>
<we-row>
<we-title
title="'Alt tag' specifies an alternate text for an image, if the image cannot be displayed (slow connection, missing image, screen reader ...).">
⌙ Description
</we-title>
<we-input class="o_we_user_value_widget">
<div>
<input id="alt" required="required" t-att-value="widget.alt" type="text"/>
</div>
</we-input>
</we-row>
<we-row>
<we-title
title="'Title tag' is shown as a tooltip when you hover the picture.">
⌙ Tooltip
</we-title>
<we-input class="o_we_user_value_widget">
<div>
<input id="title" required="required" t-att-value="widget.tag_title" type="text"/>
</div>
</we-input>
</we-row>
</we-customizeblock-option>
</t>
<!-- Media Dialog (allows to choose an img/pictogram/video) -->
<div t-name="wysiwyg.widgets.media">
<ul class="nav nav-tabs" role="tablist">
+68 -2
View File
@@ -367,8 +367,7 @@
<!-- Replace a media -->
<!-- TODO probably review this system once the new editor is merged to not duplicate the selector, etc -->
<!-- TODO enable on span.fa, i.fa too but in current editor, this is problematic so best not waste more time for now -->
<div data-js="ReplaceMedia" data-selector="img, .media_iframe_video">
<div data-js="ReplaceMedia" data-selector="img, .media_iframe_video, span.fa, i.fa">
<!-- Overlay button -->
<we-button data-name="replace_media_overlay_opt" title="Replace" class="fa fa-refresh" data-replace-media="true" data-no-preview="true"/>
<!-- Panel button-->
@@ -377,6 +376,73 @@
</we-row>
</div>
<div data-js="FontawesomeTools" data-selector="span.fa, i.fa">
<we-colorpicker string="Color"
title="Color"
data-select-style=""
data-css-property="color"
data-color-prefix="text-"/>
<we-colorpicker string="Background Color"
title="Background Color"
data-select-style=""
data-css-property="background-color"
data-color-prefix="bg-"/>
<we-button-group string="Size">
<we-button data-set-size="fa-1x" title="Size 1x">1x</we-button>
<we-button data-set-size="fa-2x" title="Size 2x">2x</we-button>
<we-button data-set-size="fa-3x" title="Size 3x">3x</we-button>
<we-button data-set-size="fa-4x" title="Size 4x">4x</we-button>
<we-button data-set-size="fa-5x" title="Size 5x">5x</we-button>
</we-button-group>
</div>
<div data-js="ImageTools" data-selector="img">
<we-input class="o_we_large_input" string="Description" data-set-alt="" placeholder="Alt tag"
title="'Alt tag' specifies an alternate text for an image, if the image cannot be displayed (slow connection, missing image, screen reader ...)."/>
<we-input class="o_we_large_input" string="Tooltip" data-set-title="" placeholder="Title tag"
title="'Title tag' is shown as a tooltip when you hover the picture."/>
<we-row string="Transform">
<we-button data-crop="" data-no-preview="true" title="Crop Image"><i class="fa fa-crop"></i></we-button>
<we-button data-transform="" data-no-preview="true" title="Transform the picture (click twice to reset transformation)">
<i class="fa fa-object-ungroup"></i>
</we-button>
</we-row>
<we-button-group string="Width">
<we-button data-set-width="" title="Resize Auto">Auto</we-button>
<we-button data-set-width="25%" title="Resize Quarter">25%</we-button>
<we-button data-set-width="50%" title="Resize Half">50%</we-button>
<we-button data-set-width="100%" title="Resize Full">100%</we-button>
</we-button-group>
</div>
<div data-js="StaticMediaTools" data-selector="span.fa, i.fa, img">
<we-select string="Alignment">
<we-button data-align="" title="Unalign">None</we-button>
<we-button data-align="left" title="Align Left">Left</we-button>
<we-button data-align="center" title="Align Center">Center</we-button>
<we-button data-align="right" title="Align Right">Right</we-button>
</we-select>
<we-row string="Shape">
<we-button data-toggle-shape-rounded="rounded" title="Shape: Rounded"><i class="fa fa-square fa-fw"></i></we-button>
<we-button data-toggle-shape-circle="rounded-circle" title="Shape: Circle"><i class="fa fa-circle-o fa-fw"></i></we-button>
<we-button data-toggle-shape-shadow="shadow" title="Shadow"><i class="fa fa-sun-o fa-fw"></i></we-button>
<we-button data-toggle-shape-thumbnail="img-thumbnail" title="Shape: Thumbnail"><i class="fa fa-picture-o fa-fw"></i></we-button>
</we-row>
<we-select string="Padding">
<we-button data-set-padding="" title="Padding: None">None</we-button>
<we-button data-set-padding="padding-small" title="Padding: Small">Small</we-button>
<we-button data-set-padding="padding-medium" title="Padding: Medium">Medium</we-button>
<we-button data-set-padding="padding-large" title="Padding: Large">Large</we-button>
<we-button data-set-padding="padding-xl" title="Padding: XL">Extra Large</we-button>
</we-select>
</div>
<div data-js="ImageOptimize"
data-selector="img">
<t t-call="web_editor.snippet_options_image_optimization_widgets"/>