summaryrefslogtreecommitdiff
path: root/addons/web_editor/static/src
diff options
context:
space:
mode:
Diffstat (limited to 'addons/web_editor/static/src')
-rw-r--r--addons/web_editor/static/src/js/editor/editor.js4
-rw-r--r--addons/web_editor/static/src/js/editor/snippets.editor.js511
-rw-r--r--addons/web_editor/static/src/js/editor/snippets.options.js121
-rw-r--r--addons/web_editor/static/src/scss/web_editor.backend.scss7
-rw-r--r--addons/web_editor/static/src/scss/web_editor.variables.scss72
-rw-r--r--addons/web_editor/static/src/scss/wysiwyg.scss439
-rw-r--r--addons/web_editor/static/src/scss/wysiwyg_iframe.scss13
-rw-r--r--addons/web_editor/static/src/scss/wysiwyg_snippets.scss832
-rw-r--r--addons/web_editor/static/src/xml/snippets.xml38
9 files changed, 925 insertions, 1112 deletions
diff --git a/addons/web_editor/static/src/js/editor/editor.js b/addons/web_editor/static/src/js/editor/editor.js
index 07947db56e4..31c31e5ce6a 100644
--- a/addons/web_editor/static/src/js/editor/editor.js
+++ b/addons/web_editor/static/src/js/editor/editor.js
@@ -106,7 +106,9 @@ var EditorMenuBar = Widget.extend({
// Snippets menu
if (self.snippetsMenu) {
- defs.push(this.snippetsMenu.insertAfter(this.$el));
+ // TODO improve this, the editor can be relocated so here,
+ // 'insertAfter(this.$el)' won't work.
+ defs.push(this.snippetsMenu.appendTo(this.$el.parent()));
}
this.rte.editable().find('*').off('mousedown mouseup click');
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 1f0bda9a1fc..fdb79ebd04a 100644
--- a/addons/web_editor/static/src/js/editor/snippets.editor.js
+++ b/addons/web_editor/static/src/js/editor/snippets.editor.js
@@ -1,6 +1,7 @@
odoo.define('web_editor.snippet.editor', function (require) {
'use strict';
+var concurrency = require('web.concurrency');
var core = require('web.core');
var dom = require('web.dom');
var Widget = require('web.Widget');
@@ -22,13 +23,9 @@ var SnippetEditor = Widget.extend({
template: 'web_editor.snippet_overlay',
xmlDependencies: ['/web_editor/static/src/xml/snippets.xml'],
events: {
- 'click .oe_snippet_parent': '_onParentButtonClick',
- 'click .oe_snippet_clone': '_onCloneClick',
- 'click .oe_snippet_remove': '_onRemoveClick',
- 'mousedown .oe_options [data-toggle="dropdown"]:first': '_onOpenCusomize',
+ 'mousedown': '_onUIMouseDown',
},
custom_events: {
- cover_update: '_onCoverUpdate',
'option_update': '_onOptionUpdate',
},
@@ -49,57 +46,57 @@ var SnippetEditor = Widget.extend({
this.$target = $(target);
this.$target.data('snippet-editor', this);
this.templateOptions = templateOptions;
+ this.isTargetParentEditable = false;
+ this.isTargetMovable = false;
},
/**
* @override
*/
start: function () {
- var self = this;
var defs = [this._super.apply(this, arguments)];
- // Hide parent button if no parent
- var $parent = globalSelector.closest(this.$target.parent());
- if (!$parent.length) {
- this.$('.oe_snippet_parent').remove();
- }
-
// Initialize the associated options (see snippets.options.js)
defs.push(this._initializeOptions());
+ var $customize = this._customize$Elements[this._customize$Elements.length - 1];
+
+ this.isTargetParentEditable = this.$target.parent().is(':o_editable');
+ this.isTargetMovable = this.isTargetParentEditable && this.isTargetMovable;
// Initialize move/clone/remove buttons
- if (!this.$target.is(':o_editable')) {
- this.$el.find('.oe_snippet_move, .oe_snippet_clone, .oe_snippet_remove').remove();
- } else {
+ if (this.isTargetMovable) {
this.dropped = false;
this.$el.draggable({
greedy: true,
appendTo: this.$body,
cursor: 'move',
- handle: '.oe_snippet_move',
+ handle: '.o_move_handle',
cursorAt: {
left: 18,
top: 14
},
- helper: function () {
- var $clone = $(this).clone().css({width: '24px', height: '24px', border: 0});
- $clone.find('.oe_overlay_options >:not(:contains(.oe_snippet_move)), .o_handle').remove();
- $clone.find(':not(.glyphicon)').css({position: 'absolute', top: 0, left: 0});
- $clone.appendTo(self.$body).removeClass('d-none');
+ helper: () => {
+ var $clone = this.$el.clone().css({width: '24px', height: '24px', border: 0});
+ $clone.appendTo(this.$body).removeClass('d-none');
return $clone;
},
- start: _.bind(self._onDragAndDropStart, self),
- stop: _.bind(self._onDragAndDropStop, self)
+ start: this._onDragAndDropStart.bind(this),
+ stop: this._onDragAndDropStop.bind(this),
});
+ } else {
+ this.$('.o_move_handle').addClass('d-none');
+ $customize.find('.oe_snippet_clone').addClass('d-none');
}
- this.$target.on('transitionstart.snippet_editor, animationstart.snippet_editor', function () {
- self._targetIsAnimated = true;
+ if (!this.isTargetParentEditable) {
+ $customize.find('.oe_snippet_remove').addClass('d-none');
+ }
+
+ this.$target.on('transitionstart.snippet_editor, animationstart.snippet_editor', () => {
+ this._targetIsAnimated = true;
});
- this.$target.on('transitionend.snippet_editor, animationend.snippet_editor', function () {
- self._targetIsAnimated = false;
- if (self.$el.is('.oe_active')) {
- self.cover();
- }
+ this.$target.on('transitionend.snippet_editor, animationend.snippet_editor', () => {
+ this._targetIsAnimated = false;
+ this.cover();
});
return Promise.all(defs);
@@ -143,9 +140,10 @@ var SnippetEditor = Widget.extend({
* Makes the editor overlay cover the associated snippet.
*/
cover: function () {
- if (this._targetIsAnimated) {
+ if (this._targetIsAnimated || !this.isShown() || !this.$target.length) {
// Do not cover a target being animated, it will be covered once the
- // animation is completed.
+ // animation is completed. Also ignore if the overlay is not shown
+ // yet or that the $target element has been removed.
return;
}
var offset = this.$target.offset();
@@ -161,6 +159,24 @@ var SnippetEditor = Widget.extend({
this.$el.toggleClass('o_top_cover', offset.top < this.$editable.offset().top);
},
/**
+ * @returns {boolean}
+ */
+ isOpened: function () {
+ return this.$el && this.$el.hasClass('o_we_options_opened');
+ },
+ /**
+ * @return {boolean}
+ */
+ isShown: function () {
+ return this.$el && this.$el.parent().length && this.$el.hasClass('oe_active');
+ },
+ /**
+ * @return {boolean}
+ */
+ isSticky: function () {
+ return this.$el && this.$el.hasClass('o_we_sticky');
+ },
+ /**
* Removes the associated snippet from the DOM and destroys the associated
* editor (itself).
*/
@@ -176,6 +192,7 @@ var SnippetEditor = Widget.extend({
},
});
+ this.trigger_up('go_to_parent', {$snippet: this.$target});
var $parent = this.$target.parent();
this.$target.find('*').addBack().tooltip('dispose');
this.$target.remove();
@@ -217,51 +234,25 @@ var SnippetEditor = Widget.extend({
* snippet options are moved to the editor overlay.
*
* @param {boolean} focus - true to display, false to hide
+ * @param {boolean} [previewMode=false]
+ * @param {boolean} [onlyOverlay=false]
*/
- toggleFocus: function (focus) {
+ toggleFocus: function (focus, previewMode, onlyOverlay) {
if (!this.$el) {
return;
}
- var do_action = (focus ? _do_action_focus : _do_action_blur);
-
- // Attach own and parent options on the current overlay
- var $style_button = this.$el.find('.oe_options');
- var $ul = $style_button.find('.dropdown-menu:first');
- var $headers = $ul.find('.dropdown-header:data(editor)');
- _.each($headers, (function (el) {
- var $el = $(el);
- var styles = _.values($el.data('editor').styles);
- if ($el.data('editor') !== this) {
- styles = _.filter(styles, function (option) { return !option.preventChildPropagation; });
- }
- var count = 0;
- _.each(_.sortBy(styles, '__order').reverse(), function (style) {
- if (do_action(style, $el)) {
- count++;
- }
- });
- $el.toggleClass('d-none', count === 0);
- }).bind(this));
-
- // Activate the overlay
- $style_button.toggleClass('d-none', $ul.children(':not(.o_main_header):not(.dropdown-divider):not(.d-none)').length === 0);
- this.cover();
- this.$el.toggleClass('oe_active', !!focus);
-
- if (focus) {
- this.trigger_up('snippet_focused');
+ if (!previewMode && !onlyOverlay) {
+ this._toggleOptions(focus ? (this._customize$Elements.length - 1) : false);
}
-
- function _do_action_focus(style, $dest) {
- style.$el.insertAfter($dest);
- style.onFocus();
- return (style.$el.length > 0);
- }
- function _do_action_blur(style, $dest) {
- style.$el.detach();
- style.onBlur();
- return false;
+ this._toggleOverlay(!!focus, previewMode ? undefined : !!focus);
+ },
+ /**
+ * @param {boolean} [isTextEdition=false]
+ */
+ toggleTextEdition: function (isTextEdition) {
+ if (this.$el) {
+ this.$el.toggleClass('o_keypress', !!isTextEdition && this.isShown());
}
},
@@ -291,58 +282,112 @@ var SnippetEditor = Widget.extend({
* @private
*/
_initializeOptions: function () {
- var self = this;
- var $styles = this.$el.find('.oe_options');
- var $ul = $styles.find('.dropdown-menu:first');
+ this._customize$Elements = [];
this.styles = {};
this.selectorSiblings = [];
this.selectorChildren = [];
+ var $element = this.$target.parent();
+ while ($element.length) {
+ var parentEditor = $element.data('snippet-editor');
+ if (parentEditor) {
+ this._customize$Elements = this._customize$Elements
+ .concat(parentEditor._customize$Elements);
+ break;
+ }
+ $element = $element.parent();
+ }
+
+ var $optionsSection = $(core.qweb.render('web_editor.customize_block_options_section', {
+ name: this._getName(),
+ })).data('editor', this);
+ $optionsSection.on('mouseover', this._onOptionsSectionMouseOver.bind(this));
+ $optionsSection.on('mouseleave', this._onOptionsSectionMouseLeave.bind(this));
+ $optionsSection.on('click', this._onOptionsSectionClick.bind(this));
+ $optionsSection.on('click', '.oe_snippet_clone', this._onCloneClick.bind(this));
+ $optionsSection.on('click', '.oe_snippet_remove', this._onRemoveClick.bind(this));
+ this._customize$Elements.push($optionsSection);
+
var i = 0;
- $ul.append($('<div/>', {class: 'dropdown-header o_main_header', text: this._getName()}).data('editor', this));
- var defs = _.map(this.templateOptions, function (val, option_id) {
- if (!val.selector.is(self.$target)) {
+ var defs = _.map(this.templateOptions, val => {
+ if (!val.selector.is(this.$target)) {
return;
}
- if (val['drop-near']) self.selectorSiblings.push(val['drop-near']);
- if (val['drop-in']) self.selectorChildren.push(val['drop-in']);
+ if (val['drop-near']) {
+ this.selectorSiblings.push(val['drop-near']);
+ }
+ if (val['drop-in']) {
+ this.selectorChildren.push(val['drop-in']);
+ }
var optionName = val.option;
var $el = val.$el.children().clone(true).addClass('snippet-option-' + optionName);
var option = new (options.registry[optionName] || options.Class)(
- self,
- val.base_target ? self.$target.find(val.base_target).eq(0) : self.$target,
- self.$el,
+ this,
+ val.base_target ? this.$target.find(val.base_target).eq(0) : this.$target,
+ this.$el,
val.data,
- self.options
+ this.options
);
- self.styles[optionName || _.uniqueId('option')] = option;
+ this.styles[optionName || _.uniqueId('option')] = option;
option.__order = i++;
- return option.attachTo($el);
+ var $optionSection = $(core.qweb.render('web_editor.customize_block_option'));
+ $optionSection.append($el);
+ return option.attachTo($optionSection);
});
- $ul.append($('<div/>', {class: 'dropdown-divider mt-2'}));
- var $parents = this.$target.parents();
- _.each($parents, function (parent) {
- var parentEditor = $(parent).data('snippet-editor');
- if (parentEditor) {
- for (var styleName in parentEditor.styles) {
- if (!parentEditor.styles[styleName].preventChildPropagation) {
- $ul.append($('<div/>', {class: 'dropdown-header o_parent_editor_header', text: parentEditor._getName()}).data('editor', parentEditor));
- break;
- }
- }
- }
- });
-
- if (!this.selectorSiblings.length && !this.selectorChildren.length) {
- this.$el.find('.oe_snippet_move, .oe_snippet_clone').addClass('d-none');
- }
+ this.isTargetMovable = (this.selectorSiblings.length > 0 || this.selectorChildren.length > 0);
this.$el.find('[data-toggle="dropdown"]').dropdown();
return Promise.all(defs);
},
+ /**
+ * @private
+ * @param {integer|false} index
+ */
+ _toggleOptions: function (index) {
+ if (index === false || !this._customize$Elements[index].parent().length) {
+ this.trigger_up('update_customize_elements', {
+ customize$Elements: index !== false ? this._customize$Elements : [],
+ });
+ }
+ this._customize$Elements.forEach(($el, i) => {
+ var open = (i === index);
+
+ $el.toggleClass('o_we_closed', !open);
+
+ var editor = $el.data('editor');
+ editor.$el.toggleClass('o_we_options_opened', open);
+
+ var styles = _.values(editor.styles);
+ $el.toggleClass('d-none', styles.length === 0);
+ _.each(_.sortBy(styles, '__order').reverse(), function (style) {
+ if (open) {
+ style.$el.appendTo($el);
+ style.onFocus();
+ } else {
+ style.$el.detach();
+ style.onBlur();
+ }
+ });
+ });
+ },
+ /**
+ * @private
+ * @param {boolean} show - whether the overlay should be shown or not
+ * @param {boolean} [sticky]
+ * whether the overlay should be sticky or not (if the value is
+ * undefined, the stickyness is left unchanged)
+ */
+ _toggleOverlay: function (show, sticky) {
+ this.$el.toggleClass('oe_active', show);
+ this.cover();
+
+ if (sticky !== undefined) {
+ this.$el.toggleClass('o_we_sticky', sticky);
+ }
+ },
//--------------------------------------------------------------------------
// Handlers
@@ -356,7 +401,7 @@ var SnippetEditor = Widget.extend({
*/
_onCloneClick: function (ev) {
ev.preventDefault();
- this.trigger_up('cover_will_change');
+ ev.stopPropagation();
this.trigger_up('snippet_will_be_cloned', {$target: this.$target});
@@ -378,15 +423,6 @@ var SnippetEditor = Widget.extend({
$clone.trigger('content_changed');
},
/**
- * Called when the overlay dimensions/positions should be recomputed.
- *
- * @private
- */
- _onCoverUpdate: function () {
- this.trigger_up('cover_will_change');
- this.cover();
- },
- /**
* Called when the snippet is starting to be dragged thanks to the 'move'
* button.
*
@@ -440,8 +476,6 @@ var SnippetEditor = Widget.extend({
self.dropped = false;
},
});
-
- this.trigger_up('cover_will_change');
},
/**
* Called when the snippet is dropped after being dragged thanks to the
@@ -504,6 +538,47 @@ var SnippetEditor = Widget.extend({
});
},
/**
+ * @private
+ */
+ _onOptionsSectionMouseOver: function (ev) {
+ var $optionsSection = $(ev.currentTarget);
+ if (!$optionsSection.hasClass('o_we_closed')) {
+ return;
+ }
+
+ this.trigger_up('activate_snippet', {
+ $snippet: this.$target,
+ previewMode: true,
+ });
+ },
+ /**
+ * @private
+ */
+ _onOptionsSectionMouseLeave: function (ev) {
+ this.trigger_up('activate_snippet', {
+ $snippet: false,
+ previewMode: true,
+ });
+ },
+ /**
+ * @private
+ */
+ _onOptionsSectionClick: function (ev) {
+ var $optionsSection = $(ev.currentTarget);
+ if (!$optionsSection.hasClass('o_we_closed')) {
+ return;
+ }
+
+ this.trigger_up('activate_snippet', {
+ $snippet: this.$target,
+ previewMode: false,
+ onlyOverlay: true,
+ });
+ var $neighbors = $optionsSection.nextAll('we-customizeblock-options');
+ var editor = $neighbors.length ? $neighbors.last().data('editor') : this;
+ editor._toggleOptions($optionsSection.index());
+ },
+ /**
* Called when a child editor/option asks for another option to perform a
* specific action/react to a specific event.
*
@@ -511,7 +586,6 @@ var SnippetEditor = Widget.extend({
* @param {OdooEvent} ev
*/
_onOptionUpdate: function (ev) {
- this.trigger_up('cover_will_change');
// If multiple option names are given, we suppose it should not be
// propagated to parent editor
if (ev.data.optionNames) {
@@ -535,28 +609,6 @@ var SnippetEditor = Widget.extend({
}
},
/**
- * Called when the user opens the customize menu.
- *
- * @private
- * @param {OdooEvent} ev
- */
- _onOpenCusomize: function (ev) {
- this.trigger_up('request_history_undo_record', {$target: this.$target});
- },
-
- /**
- * Called when the 'parent' button is clicked.
- *
- * @private
- * @param {Event} ev
- */
- _onParentButtonClick: function (ev) {
- ev.preventDefault();
- this.trigger_up('go_to_parent', {
- $snippet: this.$target,
- });
- },
- /**
* Called when the 'remove' button is clicked.
*
* @private
@@ -564,10 +616,24 @@ var SnippetEditor = Widget.extend({
*/
_onRemoveClick: function (ev) {
ev.preventDefault();
- this.trigger_up('cover_will_change');
+ ev.stopPropagation();
this.trigger_up('request_history_undo_record', {$target: this.$target});
this.removeSnippet();
},
+ /**
+ * The UI is displayed on hover and become sticky once the related target is
+ * clicked. If we click the UI while it is not sticky, it should become
+ * sticky.
+ *
+ * @private
+ */
+ _onUIMouseDown: function (ev) {
+ if (!this.isOpened()) {
+ this.trigger_up('activate_snippet', {
+ $snippet: this.$target,
+ });
+ }
+ }
});
/**
@@ -576,16 +642,21 @@ var SnippetEditor = Widget.extend({
var SnippetsMenu = Widget.extend({
id: 'oe_snippets',
cacheSnippetTemplate: {},
- activeSnippets: [],
+ events: {
+ 'click we-collapse-area > we-toggler': '_onCollapseTogglerClick',
+ },
custom_events: {
'activate_insertion_zones': '_onActivateInsertionZones',
+ 'activate_snippet': '_onActivateSnippet',
'call_for_each_child_snippet': '_onCallForEachChildSnippet',
+ 'cover_update': '_onOverlaysCoverUpdate',
'deactivate_snippet': '_onDeactivateSnippet',
'drag_and_drop_stop': '_onDragAndDropStop',
'go_to_parent': '_onGoToParent',
'remove_snippet': '_onRemoveSnippet',
'snippet_removed': '_onSnippetRemoved',
'reload_snippet_dropzones': '_disableUndroppableSnippets',
+ 'update_customize_elements': '_onUpdateCustomizeElements',
},
/**
@@ -613,8 +684,8 @@ var SnippetsMenu = Widget.extend({
if (!this.options.snippets) {
this.options.snippets = 'web_editor.snippets';
}
- this.$activeSnippet = false;
this.snippetEditors = [];
+ this._activateSnippetMutex = new concurrency.Mutex();
this.setSelectorEditableArea(options.$el, options.selectorEditableArea);
},
@@ -622,7 +693,6 @@ var SnippetsMenu = Widget.extend({
* @override
*/
start: function () {
- var self = this;
var defs = [this._super.apply(this, arguments)];
this.ownerDocument = this.$el[0].ownerDocument;
this.$document = $(this.ownerDocument);
@@ -631,8 +701,12 @@ var SnippetsMenu = Widget.extend({
// Fetch snippet templates and compute it
- defs.push(this.loadSnippets().then(function (html) {
- return self._computeSnippetTemplates(html);
+ defs.push(this.loadSnippets().then(html => {
+ return this._computeSnippetTemplates(html);
+ }).then(() => {
+ this.customizePanel = document.createElement('div');
+ this.customizePanel.classList.add('o_we_customize_panel', 'd-none');
+ this.$el.append(this.customizePanel);
}));
// Prepare snippets editor environment
@@ -653,7 +727,14 @@ var SnippetsMenu = Widget.extend({
});
var $target = $(srcElement);
- if ($target.closest('.oe_overlay, .note-popover').length) {
+ if ($target.closest('#snippets_menu').length) {
+ this._activateSnippet(false);
+ return;
+ }
+ if (!$target.closest('body > *').length) {
+ return;
+ }
+ if ($target.closest('#web_editor-top-edit, #oe_snippets, #oe_manipulators, .o_technical_modal, .oe_drop_zone').length) {
return;
}
this._activateSnippet($target);
@@ -663,23 +744,23 @@ var SnippetsMenu = Widget.extend({
core.bus.on('snippet_editor_clean_for_save', this, this._onCleanForSaveDemand);
// Adapt overlay covering when the window is resized / content changes
- var debouncedCoverUpdate = _.debounce(function () {
- self.updateCurrentSnippetEditorOverlay();
- }, 200);
+ var debouncedCoverUpdate = _.throttle(() => {
+ this.updateCurrentSnippetEditorOverlay();
+ }, 50);
this.$window.on('resize.snippets_menu', debouncedCoverUpdate);
this.$window.on('content_changed.snippets_menu', debouncedCoverUpdate);
// On keydown add a class on the active overlay to hide it and show it
// again when the mouse moves
- this.$document.on('keydown.snippets_menu', function () {
- if (self.$activeSnippet && self.$activeSnippet.data('snippet-editor')) {
- self.$activeSnippet.data('snippet-editor').$el.addClass('o_keypress');
- }
+ this.$document.on('keydown.snippets_menu', () => {
+ this.snippetEditors.forEach(editor => {
+ editor.toggleTextEdition(true);
+ });
});
- this.$document.on('mousemove.snippets_menu, mousedown.snippets_menu', function () {
- if (self.$activeSnippet && self.$activeSnippet.data('snippet-editor')) {
- self.$activeSnippet.data('snippet-editor').$el.removeClass('o_keypress');
- }
+ this.$document.on('mousemove.snippets_menu, mousedown.snippets_menu', () => {
+ this.snippetEditors.forEach(editor => {
+ editor.toggleTextEdition(false);
+ });
});
// Auto-selects text elements with a specific class and remove this
@@ -694,12 +775,19 @@ var SnippetsMenu = Widget.extend({
$(range && range.sc).closest('.o_default_snippet_text').removeClass('o_default_snippet_text');
});
- return Promise.all(defs).then(function () {
+ return Promise.all(defs).then(() => {
+ this.$('[data-title]').tooltip({
+ delay: 0,
+ title: function () {
+ return this.classList.contains('active') ? false : this.dataset.title;
+ },
+ });
+
// Trigger a resize event once entering edit mode as the snippets
// menu will take part of the screen width (delayed because of
// animation). (TODO wait for real animation end)
- setTimeout(function () {
- self.$window.trigger('resize');
+ setTimeout(() => {
+ this.$window.trigger('resize');
}, 1000);
});
},
@@ -782,11 +870,9 @@ var SnippetsMenu = Widget.extend({
* Updates the cover dimensions of the current snippet editor.
*/
updateCurrentSnippetEditorOverlay: function () {
- if (this.$activeSnippet && this.$activeSnippet.data('snippet-editor')) {
- this.$activeSnippet.data('snippet-editor').cover();
- }
this.snippetEditors = _.filter(this.snippetEditors, function (snippetEditor) {
if (snippetEditor.$target.closest('body').length) {
+ snippetEditor.cover();
return true;
}
snippetEditor.destroy();
@@ -946,38 +1032,36 @@ var SnippetsMenu = Widget.extend({
* first parent which is one is used instead.
*
* @param {jQuery|false} $snippet
- * The DOM element whose editor need to be enabled. Only disable the
- * current one if false is given.
+ * The DOM element whose editor (and its parent ones) need to be
+ * enabled. Only disable the current one if false is given.
+ * @param {boolean} [previewMode=false]
+ * @param {boolean} [onlyOverlay=false]
* @returns {Promise<SnippetEditor>}
* (might be async when an editor must be created)
*/
- _activateSnippet: function ($snippet) {
- if ($snippet) {
- if (!globalSelector.is($snippet)) {
+ _activateSnippet: function ($snippet, previewMode, onlyOverlay) {
+ return this._activateSnippetMutex.exec(() => {
+ // First disable all shown editors and also sticky ones if not in
+ // preview mode (so new sticky ones have to be shown)
+ this.snippetEditors.forEach(editor => {
+ if (!previewMode || editor.isShown() && !editor.isSticky()) {
+ editor.toggleFocus(false, previewMode, onlyOverlay);
+ }
+ });
+ // Take the first parent of the provided DOM (or itself) which
+ // should have an associated snippet editor and create + enable it.
+ if ($snippet && $snippet.length) {
$snippet = globalSelector.closest($snippet);
+ if (!$snippet.length) {
+ return Promise.resolve(null);
+ }
+ return this._createSnippetEditor($snippet).then(editor => {
+ editor.toggleFocus(true, previewMode, onlyOverlay);
+ return editor;
+ });
}
- if (this.$activeSnippet && this.$activeSnippet[0] === $snippet[0]) {
- return Promise.resolve($snippet.data('snippet-editor'));
- }
- }
- var editor = null;
- if (this.$activeSnippet) {
- editor = this.$activeSnippet.data('snippet-editor');
- if (editor) {
- editor.toggleFocus(false);
- }
- this.$activeSnippet = false;
- }
- if ($snippet && $snippet.length) {
- var self = this;
- this.trigger_up('activate_snippet', $snippet);
- return this._createSnippetEditor($snippet).then(function (editor) {
- self.$activeSnippet = $snippet;
- editor.toggleFocus(true);
- return editor;
- });
- }
- return Promise.resolve(editor);
+ return Promise.resolve(null);
+ });
},
/**
* @private
@@ -1362,7 +1446,7 @@ var SnippetsMenu = Widget.extend({
}
}
- $toInsert = $baseBody.clone().data('name', $baseBody.data('name'));
+ $toInsert = $baseBody.clone().attr('data-name', $baseBody.data('name'));
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");
@@ -1390,8 +1474,6 @@ var SnippetsMenu = Widget.extend({
}
},
});
-
- self.trigger_up('cover_will_change');
},
stop: function (ev, ui) {
$toInsert.removeClass('oe_snippet_body');
@@ -1435,9 +1517,6 @@ var SnippetsMenu = Widget.extend({
editor.buildSnippet();
}).then(function () {
$target.trigger('content_changed');
- if ($target.closest('body').length) { // can be destroyed (eg in test)
- self._activateSnippet($target);
- }
});
});
} else {
@@ -1481,6 +1560,15 @@ var SnippetsMenu = Widget.extend({
this._activateInsertionZones(ev.data.$selectorSiblings, ev.data.$selectorChildren);
},
/**
+ * Called when a child editor asks to deactivate the current snippet
+ * overlay.
+ *
+ * @private
+ */
+ _onActivateSnippet: function (ev) {
+ this._activateSnippet(ev.data.$snippet, ev.data.previewMode, ev.data.onlyOverlay);
+ },
+ /**
* Called when a child editor asks to operate some operation on all child
* snippet of a DOM element.
*
@@ -1500,6 +1588,24 @@ var SnippetsMenu = Widget.extend({
this.cleanForSave();
},
/**
+ * @private
+ * @param {*} ev
+ */
+ _onCollapseTogglerClick: function (ev) {
+ this.$('we-collapse-area > we-toggler').not(ev.currentTarget).removeClass('active');
+ ev.currentTarget.classList.toggle('active');
+ },
+ /**
+ * Called when the overlay dimensions/positions should be recomputed.
+ *
+ * @private
+ */
+ _onOverlaysCoverUpdate: function () {
+ this.snippetEditors.forEach(editor => {
+ editor.cover();
+ });
+ },
+ /**
* Called when a child editor asks to deactivate the current snippet
* overlay.
*
@@ -1548,6 +1654,23 @@ var SnippetsMenu = Widget.extend({
_onSnippetRemoved: function () {
this._disableUndroppableSnippets();
},
+ /**
+ * @private
+ * @param {OdooEvent} ev
+ */
+ _onUpdateCustomizeElements: function (ev) {
+ while (this.customizePanel.firstChild) {
+ this.customizePanel.removeChild(this.customizePanel.firstChild);
+ }
+ ev.data.customize$Elements.forEach($el => {
+ this.customizePanel.appendChild($el[0]);
+ });
+ var customize = !!ev.data.customize$Elements.length;
+ this.$('#o_scroll').toggleClass('d-none', customize);
+ this.$('.o_we_add_snippet_btn').toggleClass('active', !customize);
+ this.customizePanel.classList.toggle('d-none', !customize);
+ this.$('.o_we_customize_snippet_btn').toggleClass('active', customize);
+ },
});
return {
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 893fd8adebb..63f0c38c7c3 100644
--- a/addons/web_editor/static/src/js/editor/snippets.options.js
+++ b/addons/web_editor/static/src/js/editor/snippets.options.js
@@ -18,23 +18,16 @@ var _t = core._t;
var SnippetOption = Widget.extend({
events: {
'mouseenter': '_onLinkEnter',
- 'mouseenter a': '_onLinkEnter',
+ 'mouseenter we-button': '_onLinkEnter',
'click': '_onLinkClick',
- 'click a': '_onLinkClick',
+ 'click we-button': '_onLinkClick',
'mouseleave': '_onMouseleave',
- 'mouseleave a': '_onMouseleave',
- 'mouseleave .dropdown-menu': '_onMouseleave',
+ 'mouseleave we-button': '_onMouseleave',
},
- /**
- * When editing a snippet, its options are shown alongside the ones of its
- * parent snippets. The parent options are only shown if the following flag
- * is set to false (default).
- */
- preventChildPropagation: false,
/**
- * The option `$el` is supposed to be the associated DOM element in the
- * options dropdown. The option controls another DOM element: the snippet it
+ * The option `$el` is supposed to be the associated DOM UI element.
+ * The option controls another DOM element: the snippet it
* customizes, which can be found at `$target`. Access to the whole edition
* overlay is possible with `$overlay` (this is not recommended though).
*
@@ -120,7 +113,7 @@ var SnippetOption = Widget.extend({
/**
* Default option method which allows to select one and only one class in
* the option classes set and set it on the associated snippet. The common
- * case is having a subdropdown with each item having a `data-select-class`
+ * case is having a sub-collapse with each item having a `data-select-class`
* value allowing to choose the associated class.
*
* @param {boolean|string} previewMode
@@ -131,12 +124,14 @@ var SnippetOption = Widget.extend({
* @param {jQuery} $opt - the related DOMElement option
*/
selectClass: function (previewMode, value, $opt) {
- var $group = $opt && $opt.parents('.dropdown-submenu').last();
+ var $group = $opt && $opt.parents('we-collapse-area').last();
if (!$group || !$group.length) {
$group = this.$el;
}
- var $lis = $group.find('[data-select-class]').addBack('[data-select-class]');
- var classes = $lis.map(function () {return $(this).data('selectClass');}).get().join(' ');
+ var $lis = $group.find('[data-select-class]');
+ var classes = $lis.map(function () {
+ return $(this).data('selectClass');
+ }).get().join(' ');
this.$target.removeClass(classes);
if (value) {
@@ -146,15 +141,19 @@ var SnippetOption = Widget.extend({
/**
* Default option method which allows to select one or multiple classes in
* the option classes set and set it on the associated snippet. The common
- * case is having a subdropdown with each item having a `data-toggle-class`
+ * case is having a sub-collapse with each item having a `data-toggle-class`
* value allowing to toggle the associated class.
*
* @see this.selectClass
*/
toggleClass: function (previewMode, value, $opt) {
- var $lis = this.$el.find('[data-toggle-class]').addBack('[data-toggle-class]');
- var classes = $lis.map(function () {return $(this).data('toggleClass');}).get().join(' ');
- var activeClasses = $lis.filter('.active, :has(.active)').map(function () {return $(this).data('toggleClass');}).get().join(' ');
+ var $lis = this.$el.find('[data-toggle-class]');
+ var classes = $lis.map(function () {
+ return $(this).data('toggleClass');
+ }).get().join(' ');
+ var activeClasses = $lis.filter('.active, :has(.active)').map(function () {
+ return $(this).data('toggleClass');
+ }).get().join(' ');
this.$target.removeClass(classes).addClass(activeClasses);
if (value && previewMode !== 'reset') {
@@ -168,7 +167,7 @@ var SnippetOption = Widget.extend({
/**
* Override the helper method to search inside the $target element instead
- * of the dropdown item element.
+ * of the UI item element.
*
* @override
*/
@@ -211,7 +210,7 @@ var SnippetOption = Widget.extend({
*/
_reset: function () {
var self = this;
- var $actives = this.$el.find('.active').addBack('.active');
+ var $actives = this.$el.find('we-button.active');
_.each($actives, function (activeElement) {
var $activeElement = $(activeElement);
self.__methodNames = _.without.apply(_, [self.__methodNames].concat(_.keys($activeElement.data())));
@@ -233,8 +232,6 @@ var SnippetOption = Widget.extend({
* @param {jQuery} $opt - the related DOMElement option
*/
_select: function (previewMode, $opt) {
- var self = this;
-
// Options can say they respond to strong choice
if (previewMode && ($opt.data('noPreview') || $opt.parent().data('noPreview'))) {
return;
@@ -244,7 +241,6 @@ var SnippetOption = Widget.extend({
if (!previewMode) {
this._reset();
this.trigger_up('request_history_undo_record', {$target: this.$target});
- this.$target.trigger('content_changed');
}
// Search for methods (data-...) (i.e. data-toggle-class) on the
@@ -275,6 +271,8 @@ var SnippetOption = Widget.extend({
if (!previewMode) {
this._setActive();
}
+
+ this.$target.trigger('content_changed');
},
/**
* Tweaks the option DOM elements to show the selected value according to
@@ -286,7 +284,6 @@ var SnippetOption = Widget.extend({
_setActive: function () {
var self = this;
this.$el.find('[data-toggle-class]')
- .addBack('[data-toggle-class]')
.removeClass('active')
.filter(function () {
var className = $(this).data('toggleClass');
@@ -295,25 +292,20 @@ var SnippetOption = Widget.extend({
.addClass('active');
// Get submenus which are not inside submenus
- var $submenus = this.$el.find('.dropdown-submenu')
- .addBack('.dropdown-submenu')
- .not('.dropdown-submenu .dropdown-submenu');
+ var $submenus = this.$el.find('we-collapse-area')
+ .not('we-collapse-area we-collapse-area');
// Add unique active class for each submenu active item
_.each($submenus, function (submenu) {
- var $elements = _getSelectClassElements($(submenu));
+ var $elements = $(submenu).find('[data-select-class]');
_processSelectClassElements($elements);
});
// Add unique active class for out-of-submenu active item
- var $externalElements = _getSelectClassElements(this.$el)
- .not('.dropdown-submenu *, .dropdown-submenu');
+ var $externalElements = this.$el.find('[data-select-class]')
+ .not('we-collapse-area *, we-collapse-area');
_processSelectClassElements($externalElements);
- function _getSelectClassElements($el) {
- return $el.find('[data-select-class]')
- .addBack('[data-select-class]');
- }
function _processSelectClassElements($elements) {
var maxNbClasses = -1;
$elements.removeClass('active')
@@ -343,20 +335,11 @@ var SnippetOption = Widget.extend({
* @param {Event} ev
*/
_onLinkEnter: function (ev) {
- var $opt = $(ev.target).closest('.dropdown-item');
+ var $opt = $(ev.target).closest('we-button');
if (!$opt.length) {
return;
}
- var $dsmenu = $opt.parent('.dropdown-submenu');
- if ($dsmenu.length) {
- var $menu = $dsmenu.children('.dropdown-menu'); // FIXME
- if ($menu.length) {
- var menuRightPosition = $dsmenu.offset().left + $dsmenu.outerWidth() + $menu.outerWidth();
- $menu.toggleClass('o_open_to_left', menuRightPosition > $(window).outerWidth());
- }
- }
-
if (!$opt.is(':hasData')) {
return;
}
@@ -371,7 +354,7 @@ var SnippetOption = Widget.extend({
* @param {Event} ev
*/
_onLinkClick: function (ev) {
- var $opt = $(ev.target).closest('.dropdown-item');
+ var $opt = $(ev.target).closest('we-button');
if (ev.isDefaultPrevented() || !$opt.length || !$opt.is(':hasData')) {
return;
}
@@ -403,8 +386,6 @@ var SnippetOption = Widget.extend({
var registry = {};
registry.sizing = SnippetOption.extend({
- preventChildPropagation: true,
-
/**
* @override
*/
@@ -413,11 +394,8 @@ registry.sizing = SnippetOption.extend({
var def = this._super.apply(this, arguments);
this.$handles = this.$overlay.find('.o_handle');
- var resizeValues = this._getSize();
- _.each(resizeValues, function (value, key) {
- self.$handles.filter('.' + key).toggleClass('readonly', !value);
- });
+ var resizeValues = this._getSize();
this.$handles.on('mousedown', function (ev) {
ev.preventDefault();
@@ -522,8 +500,19 @@ registry.sizing = SnippetOption.extend({
* @override
*/
onFocus: function () {
+ var resizeValues = this._getSize();
+ _.each(resizeValues, (value, key) => {
+ this.$handles.filter('.' + key).toggleClass('readonly', !value);
+ });
+
this._onResize();
},
+ /**
+ * @override
+ */
+ onBlur: function () {
+ this.$handles.addClass('readonly');
+ },
//--------------------------------------------------------------------------
// Public
@@ -596,6 +585,9 @@ registry.sizing = SnippetOption.extend({
width: ml,
left: '-' + ml,
});
+ this.$overlay.find('.o_handle.e').css({
+ width: 0,
+ });
_.each(this.$overlay.find(".o_handle.n, .o_handle.s"), function (handle) {
var $handle = $(handle);
var direction = $handle.hasClass('n') ? 'top': 'bottom';
@@ -671,7 +663,7 @@ registry.colorpicker = SnippetOption.extend({
var $clpicker = $(qweb.render('web_editor.colorpicker'));
_.each($clpicker.find('.o_colorpicker_section'), function (elem) {
- $(elem).prepend("<div class='text-muted mt8'>" + elem.dataset.display + "</div>");
+ $(elem).prepend("<div>" + elem.dataset.display + "</div>");
});
// Retrieve excluded palettes list
@@ -705,7 +697,7 @@ registry.colorpicker = SnippetOption.extend({
}
$pt.find('.o_colorpicker_section_tabs').append($clpicker);
- this.$el.find('.dropdown-menu').append($pt);
+ this.$el.find('we-collapse').append($pt);
}
var classes = [];
@@ -945,7 +937,7 @@ registry.background = SnippetOption.extend({
_setCustomBackground: function (value) {
this.__customImageSrc = value;
this.background(false, this.__customImageSrc);
- this.$target.addClass('oe_custom_bg');
+ this.$target.toggleClass('oe_custom_bg', !!value);
this._setActive();
this.$target.trigger('snippet-option-change', [this]);
},
@@ -1193,7 +1185,7 @@ registry.many2one = SnippetOption.extend({
// create search button and bind search bar
this.$btn = $(qweb.render('web_editor.many2one.button'))
- .insertAfter(this.$overlay.find('.oe_options'));
+ .prependTo(this.$el);
this.$ul = this.$btn.find('ul');
this.$search = this.$ul.find('li:first');
@@ -1203,17 +1195,10 @@ registry.many2one = SnippetOption.extend({
// move menu item
setTimeout(function () {
- if (self.$overlay.find('.oe_options').hasClass('d-none')) {
- self.$btn.css('height', '0').find('> a').addClass('d-none');
- self.$ul.show().css({
- 'top': '-24px', 'margin': '0', 'padding': '2px 0', 'position': 'relative'
- });
- } else {
- self.$btn.find('a').on('click', function (e) {
- self._clear();
- });
- }
- },0);
+ self.$btn.find('a').on('click', function (e) {
+ self._clear();
+ });
+ }, 0);
// bind search input
this.$search.find('input')
diff --git a/addons/web_editor/static/src/scss/web_editor.backend.scss b/addons/web_editor/static/src/scss/web_editor.backend.scss
index ecbe42721f3..385bb370fd7 100644
--- a/addons/web_editor/static/src/scss/web_editor.backend.scss
+++ b/addons/web_editor/static/src/scss/web_editor.backend.scss
@@ -3,7 +3,7 @@
word-wrap: break-word;
.note-editable {
- min-height: 180px;
+ min-height: 330px;
font: inherit !important;
font-family: inherit !important;
line-height: inherit !important;
@@ -23,13 +23,16 @@
width: 100%;
margin: 0;
padding: 0;
- border: 0;
ul > li > p {
margin: 0px;
}
min-height: 300px;
min-height: -webkit-calc(100vh - 170px);
min-height: calc(100vh - 170px);
+
+ &.o_readonly {
+ border: none;
+ }
}
.rounded {
border-radius: .25rem !important;
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 fd821c547c2..b03277b2b95 100644
--- a/addons/web_editor/static/src/scss/web_editor.variables.scss
+++ b/addons/web_editor/static/src/scss/web_editor.variables.scss
@@ -2,14 +2,22 @@
/// This files regroups the variables and mixins which are specific to the editor.
///
-$o-we-font-family: Roboto, 'Montserrat', "Segoe UI","Helvetica Neue",Helvetica,Arial,sans-serif !default;
+$o-we-font-family: Roboto, 'Montserrat', 'Segoe UI', 'Helvetica Neue', Helvetica, Arial, sans-serif !default;
+
+$o-we-font-size: 13px;
+$o-we-bg-color: #F7F7F7 !default;
+$o-we-bg-color-dark: #a7a7a7 !default;
+$o-we-color: #212629 !default;
+$o-we-border-width: 1px !default;
+$o-we-border-color: rgba(0, 0, 0, 0.125) !default;
+$o-we-dropdown-bg-color: #FFFFFF;
+$o-we-dropdown-color: inherit;
$o-we-color-dark: #2c2c36 !default;
$o-we-color-darker: darken($o-we-color-dark, 2%) !default;
$o-we-color-divider: #33363e !default;
$o-we-color-light: #4e525b !default;
-$o-we-color-normal: #50545d !default;
-$o-we-color-paper: #fcfcfc !default;
+$o-we-color-normal: #3D4047 !default;
$o-we-color-text-normal: #999999 !default;
$o-we-color-text-light: #d4d5d7 !default;
$o-we-color-text-lighter: #ebecee !default;
@@ -18,58 +26,38 @@ $o-we-color-warning: #f0ad4e !default;
$o-we-color-success: #40ad67 !default;
$o-we-color-info: #6999a8 !default;
-$o-we-sidebar-width: 210px !default;
+$o-we-sidebar-width: 230px !default;
-$o-we-tab-active-bg: #3D4048 !default;
+$o-we-handles-offset-to-hide: 10000px !default;
+$o-we-handles-btn-size: 18px !default;
+$o-we-handles-accent-color: #00e2ff !default;
+$o-we-handles-accent-color-preview: $o-enterprise-color !default;
+$o-we-handle-edge-size: 8px !default;
+$o-we-handle-border-width: 2px !default;
+$o-we-handle-border-width-preview: 6px !default;
// Transitions
$o-we-md-ease: cubic-bezier(0.19, 1, 0.22, 1) !default;
// Editor
-$o-we-dropzone-size: 40px !default;
+$o-we-dropzone-size: 30px !default; // $grid-gutter-width (todo: allow to use the variable)
$o-we-dropzone-border-width: 2px !default;
$o-we-dropzone-border: $o-we-dropzone-border-width dashed $o-brand-odoo !default;
-$o-we-handle-edge-size: 1px !default;
-$o-we-handle-border-width: 1px !default;
-$o-we-handles-offset-to-hide: 10000px !default;
-$o-we-handles-accent-color: #00e2ff !default;
-$o-we-handles-btn-size: 18px !default;
-
-$o-we-overlay-option-size: 25px !default;
-
$o-we-ace-color: #2F3129 !default;
$o-we-switch-size: 2ex !default;
-$o-we-switch-inactive-color: $o-we-color-paper !default;
+$o-we-switch-inactive-color: $o-we-bg-color !default;
// Checklist
$o-checklist-margin-left: 20px;
$o-checklist-checkmark-width: 2px;
$o-checklist-before-size: 13px;
-$o-wysiwyg-zindex: 1000;
-
-$o-wysiwyg-md-ease: cubic-bezier(0.19, 1, 0.22, 1) !default;
-
-$o-wysiwyg-font-family: Roboto,
-"Montserrat",
-"Segoe UI",
-"Helvetica Neue",
-Helvetica,
-Arial,
-sans-serif;
-
-$o-wysiwyg-toolbar-height: 32px;
-
-$o-wysiwyg-color: #212629 !default;
-$o-wysiwyg-color-light: #616669 !default;
-$o-wysiwyg-color-danger: #912629 !default;
-$o-wysiwyg-color-success: #219629 !default;
-$o-wysiwyg-bg-color: #f7f7f7 !default;
-$o-wysiwyg-bg-color-dark: #a7a7a7 !default;
-$o-wysiwyg-border-color: #a9a9a9 !default;
+$o-we-zindex: 1000;
+$o-we-overlay-zindex: $o-we-zindex - 10;
+$o-we-toolbar-height: 32px;
// ============ 'Preview' component Mixins ===============
// Create a "preview" work-area to edit/customize specific elements
@@ -191,7 +179,7 @@ $o-wysiwyg-border-color: #a9a9a9 !default;
.modal-content {
border-radius: 0;
- background-color: $o-we-color-paper;
+ background-color: $o-we-bg-color;
.modal-header {
border-bottom-color: #e9ecef;
@@ -222,7 +210,7 @@ $o-wysiwyg-border-color: #a9a9a9 !default;
&.active {
&, &:hover, &:focus {
color: $o-we-color-normal;
- background-color: $o-we-color-paper;
+ background-color: $o-we-bg-color;
border-color: #dee2e6 #dee2e6 #FFFFFF;
}
}
@@ -242,11 +230,11 @@ $o-wysiwyg-border-color: #a9a9a9 !default;
@include button-variant($o-brand-primary, $o-brand-primary);
}
&.btn-secondary {
- @include button-variant($o-we-color-paper, $o-we-color-paper);
+ @include button-variant($o-we-bg-color, $o-we-bg-color);
color: $o-brand-primary;
}
&.btn-link {
- @include button-variant($o-we-color-paper, $o-we-color-paper);
+ @include button-variant($o-we-bg-color, $o-we-bg-color);
color: $o-brand-primary;
}
&.btn-success {
@@ -268,8 +256,8 @@ $o-wysiwyg-border-color: #a9a9a9 !default;
.card {
padding: 19px;
margin-bottom: 20px;
- background-color: $o-we-color-paper;
- border: 1px solid darken($o-we-color-paper, 5%);
+ background-color: $o-we-bg-color;
+ border: 1px solid darken($o-we-bg-color, 5%);
border-radius: 0;
box-shadow: none;
}
diff --git a/addons/web_editor/static/src/scss/wysiwyg.scss b/addons/web_editor/static/src/scss/wysiwyg.scss
index cd8fa7bcf44..90180133f12 100644
--- a/addons/web_editor/static/src/scss/wysiwyg.scss
+++ b/addons/web_editor/static/src/scss/wysiwyg.scss
@@ -1,322 +1,168 @@
-// NOTE EDITOR
+// Use css variables to control the default style of the editor so that an
+// external assets bundle can influence it without duplicating the css.
+:root {
+ --o-we-toolbar-height: #{$o-we-toolbar-height};
-body.editor_enable {
- padding-top: $o-wysiwyg-toolbar-height !important;
-}
+ --o-we-bg-color: #{$o-we-bg-color};
+ --o-we-bg-color-dark: #{$o-we-bg-color-dark};
+ --o-we-color: #{$o-we-color};
+ --o-we-color-accent: #{$o-we-color};
-#wrapwrap {
- height: calc(100vh - #{$o-wysiwyg-toolbar-height});
- border: 1px solid $o-wysiwyg-border-color;
- border-left: 0;
- border-top: 0;
+ --o-we-dropdown-bg-color: #{$o-we-dropdown-bg-color};
+ --o-we-dropdown-color: #{$o-we-dropdown-color};
- .btn {
- -webkit-user-select: none;
- }
+ --o-we-snippets-bg-color: #{$o-we-bg-color};
+ --o-we-snippets-color: #{$o-we-color};
}
-// TOP BAR (EDIT)
+// EDITOR TOP BAR AND POPOVER
+body.editor_enable {
+ padding-top: var(--o-we-toolbar-height) !important;
+}
#web_editor-top-edit {
- height: $o-wysiwyg-toolbar-height;
- background-color: $o-wysiwyg-bg-color;
- z-index: $o-wysiwyg-zindex - 2;
+ @include o-position-absolute(0, 0, auto, 0);
position: fixed;
- top: 0px;
- right: 0px;
- left: 0px;
- white-space: nowrap;
- border: 1px solid $o-wysiwyg-border-color;
- border-bottom: 1px solid rgba(0, 0, 0, 0.125);
-
- transition: background-color 400ms $o-we-md-ease 0s;
- font-family: $o-we-font-family;
-}
-
-.note-editor.note-frame {
- background-color: inherit;
-
- .note-editing-area .note-editable {
- background-color: transparent !important;
- color: inherit;
- padding-left: 0;
- padding-right: 0;
- }
-
- &.fullscreen {
- background-color: #fff;
+ z-index: $o-we-zindex;
+ height: var(--o-we-toolbar-height);
+ background-color: var(--o-we-bg-color);
+
+ .note-popover .popover {
+ top: 0 !important;
+ left: 0 !important;
+ right: 0 !important;
+ border: none !important;
}
}
-.note-editor:not(.note-frame) .note-editing-area {
- overflow: visible;
-}
-
-#web_editor-top-edit .note-popover .popover {
- height: $o-wysiwyg-toolbar-height - 2;
- top: 0 !important;
- left: 0 !important;
- padding: 0;
+.wysiwyg_iframe,
+.note-editor {
+ border: $o-we-border-width solid $o-we-border-color;
margin: 0;
- border: 0;
- width: auto;
- text-align: center;
- box-shadow: none;
- background-color: $o-wysiwyg-bg-color;
- z-index: $o-wysiwyg-zindex;
-
- .popover-body {
- height: $o-wysiwyg-toolbar-height - 2;
-
- > .btn-group {
- height: $o-wysiwyg-toolbar-height - 2;
- margin-top: 0;
- }
-
- .btn {
- height: $o-wysiwyg-toolbar-height - 2;
- border-top: 0;
- }
- }
+ padding: 0;
}
+.note-popover .popover .popover-body,
+.panel-heading.note-toolbar {
+ height: var(--o-we-toolbar-height);
+ padding-bottom: 0;
+ border-bottom: $o-we-border-width solid $o-we-border-color;
+ background-color: var(--o-we-bg-color);
+ font-family: $o-we-font-family;
+ white-space: nowrap;
-#web_editor-toolbars .popover,
-.note-popover.popover .popover-content,
-.note-editor .note-toolbar {
- font-family: $o-wysiwyg-font-family;
-
- h1,
- h2,
- h3,
- h4,
- h5,
- h6,
- .h1,
- .h2,
- .h3,
- .h4,
- .h5,
- .h6 {
- font-family: $o-wysiwyg-font-family;
- }
-
- .btn {
+ // Main layout of buttons
+ .btn-group, .btn {
+ width: auto !important;
+ height: 100% !important;
+ margin-top: 0;
+ margin-bottom: 0;
+ background: transparent;
border: none;
border-radius: 0;
- background-color: inherit;
+ }
+ .btn-secondary {
color: inherit;
- transition: all 0.3s ease 0s;
+ }
+
+ // Active buttons and opened dropdowns
+ .btn {
+ padding: 0.5em 0.75em !important;
+ border-left: 1px solid transparent;
+ border-right: 1px solid transparent;
+ color: var(--o-we-color);
+ font-size: $o-we-font-size !important;
- >.fa {
- color: inherit;
+ &.active {
+ background-color: var(--o-we-bg-color-dark);
+ color: var(--o-we-color-accent);
}
&:hover {
- border-top: 2px solid inherit;
- color: inherit;
-
- >.fa {
- color: inherit;
- }
+ color: var(--o-we-color-accent);
}
- .o_image_alt {
- @include o-text-overflow(inline-block);
- max-width: 100px;
+ &:focus, &:active, &:focus:active {
+ @extend %we-active-button;
}
-
- &.active {
- background-color: inherit;
- color: inherit;
- box-shadow: inset 0 3px 10px rgba(0, 0, 0, 0.5);
-
- >.fa {
- color: inherit;
- }
+ }
+ .btn-group.show {
+ > .btn {
+ @extend %we-active-button;
}
-
- &.o_disabled {
- opacity: 0.5;
- pointer-events: none;
+ &::after {
+ content: '';
+ @include o-position-absolute(100%, $o-we-border-width, auto, $o-we-border-width);
+ height: $o-we-border-width;
+ background-color: var(--o-we-dropdown-bg-color);
}
}
-
- .note-btn-group {
- margin: 0;
- }
-
- >.btn-group {
- margin-top: 0;
-
- &.show,
- .btn-group.show {
- .dropdown-toggle {
- border-top: 2px solid white;
- background-color: $o-wysiwyg-bg-color-dark;
- box-shadow: none;
-
- >.fa,
- >span {
- color: inherit;
- }
- }
- }
+ %we-active-button {
+ border-left-color: $o-we-border-color;
+ border-right-color: $o-we-border-color;
+ background: var(--o-we-dropdown-bg-color);
+ box-shadow: none !important;
+ outline: none !important;
}
-
.dropdown-menu {
- margin: 0;
+ margin-top: 0;
+ border: $o-we-border-width solid $o-we-border-color;
+ border-top-color: transparent;
border-radius: 0;
- background-color: $o-wysiwyg-bg-color;
- color: inherit;
- white-space: normal;
-
- .dropdown-item {
- width: 100%;
- color: inherit;
-
- >.fa {
- color: inherit;
- }
-
- &:hover {
- background-color: $o-wysiwyg-bg-color-dark;
- color: inherit;
-
- >.fa {
- color: inherit;
- }
- }
- }
+ background-color: var(--o-we-dropdown-bg-color);
+ color: var(--o-we-dropdown-color);
+ box-shadow: none;
}
-
- .note-para .dropdown-menu {
+ .dropdown-menu.show { // To overcome .note-XXX .dropdown-menu rules
min-width: 0;
- white-space: nowrap;
}
+ .dropdown-item { // To overcome summernote rules breaking this in iframes
+ display: block;
+ max-width: none;
+ overflow: visible;
+ color: var(--o-we-color);
- .note-color {
-
- .note-back-color-preview,
- .note-fore-color-preview {
- .dropdown-toggle {
- padding: 0px 7px;
- height: 35px;
- width: auto;
- border-top: none;
- border-bottom: 3px solid;
-
- &:after {
- display: none;
- }
- }
- }
-
- .dropdown-menu {
- min-width: 180px;
-
- .note-custom-color {
- cursor: pointer;
- border-radius: 5px;
-
- &:hover {
- background-color: gray('400');
- color: black
- }
- }
- }
-
- .dropdown-toggle:after {
- display: none;
- }
-
- .note-palette:first-child {
- width: 100%;
-
- .note-color-reset,
- .note-custom-color {
- width: 162px;
- }
-
- .note-color-reset {
- padding: 0 3px !important;
- height: 32px;
- }
-
- .note-holder,
- .note-color-palette .note-color-row,
- .note-custom-color-palette .note-color-row {
- height: auto !important;
-
- &::after {
- content: "";
- display: table;
- clear: both;
- }
-
- .note-color-btn {
- float: left;
- height: 20px;
- width: 20px;
- margin: 1px 1px 0 0;
- border: 1px solid black;
- cursor: pointer;
- @include o-alpha-button-preview;
-
- &.o_small {
- width: 16px;
- height: 16px;
- margin: 3px 3px 2px 2px;
- border-radius: 50%;
- }
-
- &.o_clear {
- clear: both;
- }
+ &:hover {
+ background-color: var(--o-we-bg-color) !important;
+ color: var(--o-we-color-accent) !important;
- &.o_btn_transparent {
- display: none;
- }
- }
+ > * {
+ color: inherit !important;
}
}
}
-}
-
-.note-popover.popover.note-hint-popover .popover-content {
- height: auto;
-}
-.note-popover,
-.note-editor {
- .modal .modal-footer p {
- display: none;
+ // Specific elements
+ .o_image_alt {
+ @include o-text-overflow();
+ max-width: 150px;
}
-}
-
-.note-handle {
- pointer-events: none;
-
- .note-control-selection {
- z-index: 1;
- overflow: hidden;
-
- .note-control-se {
- border-top: 0;
- border-left: 0;
- background: transparent;
- }
+ .note-color-palette div .note-color-btn {
+ border-color: var(--o-we-dropdown-bg-color);
}
}
-.o_fake_editable:not([data-oe-model]):focus {
- outline: none;
+// ANIMATIONS
+@keyframes fadeInDownSmall {
+ 0% {
+ opacity: 0;
+ transform: translate(0, -5px);
+ }
+ 100% {
+ opacity: 1;
+ transform: translate(0, 0);
+ }
}
-.note-editor.dragover .note-dropzone {
- z-index: $o-wysiwyg-zindex + 1;
+@keyframes inputHighlighter {
+ from {
+ background: $o-brand-primary;
+ }
+ to {
+ width: 0;
+ background: transparent;
+ }
}
// MODALS
-
body .modal {
// SELECT MEDIA
@@ -362,7 +208,7 @@ body .modal {
@include o-position-absolute($top: 0, $right: 0);
border-radius: 0 0 0 2px;
&:hover {
- color: $o-wysiwyg-color-danger;
+ color: $o-we-color-danger;
}
}
@@ -390,7 +236,7 @@ body .modal {
}
.font-icons-icons {
- >span {
+ > span {
text-align: center;
font-size: 22px;
margin: 5px;
@@ -401,8 +247,8 @@ body .modal {
&.o_selected {
cursor: auto;
- background-color: $o-wysiwyg-bg-color-dark;
- box-shadow: 0px 0px 0px 1px $o-wysiwyg-color-success;
+ background-color: $o-we-bg-color-dark;
+ box-shadow: 0px 0px 0px 1px $o-we-color-success;
}
}
}
@@ -421,7 +267,7 @@ body .modal {
position: relative;
width: 100%;
- >textarea {
+ > textarea {
width: 100%;
min-height: 95px;
padding-bottom: 25px;
@@ -460,7 +306,7 @@ body .modal {
.cropper-point {
background-color: white;
- transition: all 400ms $o-wysiwyg-md-ease 0s;
+ transition: all 400ms $o-we-md-ease 0s;
transform: scale(1.5, 1.5);
border-radius: 10px;
box-shadow: 0 0 1px 1px rgba(23, 23, 23, 0.5);
@@ -482,7 +328,7 @@ body .modal {
// LINK EDITOR DIALOG COLOR SELECTOR
.o_link_dialog {
- .o_link_dialog_color>.o_link_dialog_color_item {
+ .o_link_dialog_color > .o_link_dialog_color_item {
position: relative;
width: 30px;
height: 30px;
@@ -490,7 +336,7 @@ body .modal {
border: 2px solid rgba(0, 0, 0, 0.25);
border-radius: 50%;
- input:checked+i::before {
+ input:checked + i::before {
content: "\f00c";
@include o-position-absolute($top: 6px, $left: 5px);
}
@@ -498,7 +344,7 @@ body .modal {
&.btn-link {
padding: 3px 9px;
- >span {
+ > span {
text-decoration: underline;
}
}
@@ -511,7 +357,7 @@ body .modal {
.o_we_image_optimize_dialog {
.o_we_title_label {
- font-size: 1rem;
+ font-size: $o-we-font-size;
}
.o_we_preview_area {
max-height: 400px;
@@ -539,32 +385,3 @@ img.o_we_selected_image {
img.o_we_custom_image.mx-auto {
display: block;
}
-
-// ENTER IN EDIT MODE
-
-.note-editor .css_editable_mode_display {
- border: 1px dashed rgba(153, 153, 153, 0.5);
- display: block !important;
-}
-
-.note-editable {
-
- li,
- p,
- h1,
- h2,
- h3,
- h4,
- h5,
- h6 {
- &:empty:after {
- content: "\00a0"
- }
- }
-}
-
-.wysiwyg_iframe {
- margin: 0;
- padding: 0;
- border: 0;
-}
diff --git a/addons/web_editor/static/src/scss/wysiwyg_iframe.scss b/addons/web_editor/static/src/scss/wysiwyg_iframe.scss
index e1af34e41fa..e7554317994 100644
--- a/addons/web_editor/static/src/scss/wysiwyg_iframe.scss
+++ b/addons/web_editor/static/src/scss/wysiwyg_iframe.scss
@@ -16,21 +16,8 @@ iframe.wysiwyg_iframe.o_fullscreen {
body.o_in_iframe {
background-color: $o-view-background-color;
- overflow: auto;
-
- .note-editable {
- height: calc(100vh - #{$o-wysiwyg-toolbar-height} - 2px);
- }
.note-statusbar {
display: none;
}
-
- #web_editor-toolbars .popover .btn {
- font-size: 1rem;
- }
-
- .o_mail_theme_selector .btn {
- font-size: 1rem;
- }
}
diff --git a/addons/web_editor/static/src/scss/wysiwyg_snippets.scss b/addons/web_editor/static/src/scss/wysiwyg_snippets.scss
index 6b576acecba..70665b4861f 100644
--- a/addons/web_editor/static/src/scss/wysiwyg_snippets.scss
+++ b/addons/web_editor/static/src/scss/wysiwyg_snippets.scss
@@ -2,86 +2,74 @@
/// This file contains all variables and mixins that are specific to the editor.
///
-$o-wysiwyg-handles-accent-color: #00e2ff !default;
-$o-wysiwyg-handle-edge-size: 1px !default;
-$o-wysiwyg-dropzone-size: 40px !default;
-$o-wysiwyg-sidebar-width: 210px !default;
-$o-wysiwyg-sidebar-height: 36px !default;
-$o-wysiwyg-overlay-option-size: 25px !default;
-$o-wysiwyg-handles-btn-size: 18px !default;
-$o-wysiwyg-handle-border-width: 1px !default;
-
-$o-wysiwyg-handles-offset-to-hide: 10000px !default;
-
// OVERRIDES FOR EDITOR WITH SNIPPETS
body.editor_enable.editor_has_snippets {
padding-left: $o-we-sidebar-width !important;
#web_editor-top-edit .note-popover .popover {
- margin: 0 0 0 $o-we-sidebar-width;
- }
-}
-
-// NOTE EDITOR
-.note-editor.o_snippets_loaded {
- transition: padding-left 400ms $o-wysiwyg-md-ease 0s;
- padding-left: 210px !important;
-}
-
-.note-editor.note-frame .note-editing-area .o_wrap_editable_snippets {
- overflow: auto;
-
- .note-editable {
- overflow: visible;
+ left: $o-we-sidebar-width !important;
}
}
-.o_in_iframe .note-editor.o_snippets_loaded .note-editable {
- padding-top: 30px;
-}
-
// SNIPPET PANEL
#oe_snippets {
@include o-w-preserve-btn;
+ @include o-position-absolute(0, auto, 0, -$o-we-sidebar-width);
+ position: fixed;
+ z-index: $o-we-zindex;
display: flex;
flex-flow: column nowrap;
- position: fixed;
- top: 0;
- width: $o-wysiwyg-sidebar-width;
- z-index: $o-wysiwyg-zindex - 1;
+ width: $o-we-sidebar-width;
- font-family: Roboto, $font-family-sans-serif;
- border-right: 1px solid #aaa;
- transition: left 400ms $o-wysiwyg-md-ease 0s;
- background-image: linear-gradient(45deg, $o-wysiwyg-bg-color, darken($o-wysiwyg-bg-color, 10%));
+ border-right: $o-we-border-width solid $o-we-border-color;
+ background-color: var(--o-we-snippets-bg-color);
+ color: var(--o-we-snippets-color);
box-shadow: 0px 10px 10px -10px black inset;
- border: 1px solid $o-wysiwyg-border-color;
+ font-family: Roboto, $font-family-sans-serif;
+
+ transition: left 400ms $o-we-md-ease 0s;
+
+ &.o_loaded {
+ left: 0 !important;
+ }
#snippets_menu {
flex: 0 0 auto;
- height: $o-wysiwyg-toolbar-height;
- line-height: $o-wysiwyg-sidebar-height;
- margin: 0;
- padding: 0;
- background: $o-wysiwyg-bg-color;
- text-align: center;
- font-weight: normal;
- font-size: 17px;
- color: $o-wysiwyg-color;
- font-family: $o-wysiwyg-font-family;
+ display: flex;
+ height: var(--o-we-toolbar-height);
+ background: var(--o-we-bg-color);
+ color: var(--o-we-color);
+ font-family: $o-we-font-family;
+
+ > button {
+ flex: 1 1 auto;
+ border: none;
+ background: transparent;
+ color: inherit;
+ @include o-text-overflow();
+
+ &.active {
+ background: var(--o-we-snippets-bg-color);
+ color: var(--o-we-snippets-color);
+ }
+ }
}
- #o_scroll {
+ > #o_scroll, > .o_we_customize_panel {
+ flex: 1 1 auto;
height: 100%;
overflow: auto;
+ }
+
+ > #o_scroll {
.o_panel_header {
display: flex;
align-items: center;
justify-content: center;
padding: 3%;
- color: $o-wysiwyg-color;
+ color: inherit;
margin-bottom: 3%;
i {
@@ -149,24 +137,156 @@ body.editor_enable.editor_has_snippets {
}
}
- &:nth-child(2n+1) {
+ &:nth-child(2n + 1) {
clear: left;
}
}
}
}
- &.o_loaded {
- left: 0 !important;
+ > .o_we_customize_panel {
+
+ we-button, we-toggler {
+ cursor: pointer;
+ @include o-text-overflow(inline-block);
+ padding: 0.25rem 0.5rem;
+ margin: 0;
+ background-color: var(--o-we-bg-color-dark);
+ color: var(--o-we-color);
+ line-height: 1.5;
+ font-weight: 500;
+ white-space: nowrap;
+ }
+
+ %we-icon-button {
+ position: relative;
+
+ &::after {
+ position: absolute;
+ width: 1em;
+ right: 0.5em;
+ text-align: center;
+ font-family: FontAwesome;
+ }
+ }
+
+ @mixin we-icon-button($icon, $color: null) {
+ @extend %we-icon-button;
+ padding-right: 0.5em + 1em + 0.5em;
+
+ &::after {
+ content: $icon;
+ color: $color;
+ }
+ }
+
+ we-customizeblock-options {
+ display: block;
+ border: $o-we-border-width solid var(--o-we-snippets-color);
+ color: var(--o-we-snippets-color);
+ margin-top: 0.25rem;
+
+ > we-title {
+ display: flex;
+ align-items: center;
+ margin: 0.25rem 0;
+ padding: 0 0.5rem;
+ font-weight: bold;
+ color: inherit !important;
+
+ @include we-icon-button('\f107');
+
+ > we-button-group {
+ flex: 0 0 auto;
+ display: block;
+ margin-left: auto;
+ text-align: center;
+
+ > we-button {
+ margin-left: 0.25rem;
+ width: 1.5rem;
+ padding: 0.125rem;
+ }
+ }
+ }
+ }
+
+ we-customizeblock-option {
+ position: relative;
+ display: block;
+ overflow: hidden;
+ max-height: 500px;
+ transition: max-height 200ms ease 0s;
+
+ > * {
+ display: block;
+ margin-top: 0.25rem;
+ }
+
+ we-button, we-toggler {
+ display: block;
+ text-align: left;
+ border-radius: 0;
+ }
+ we-button {
+ &.we-button-hover-icon:hover {
+ @include we-icon-button('\f040');
+ }
+
+ &.active {
+ @include we-icon-button('\f00c', $o-we-color-success);
+ }
+ }
+ we-collapse {
+ position: relative;
+ display: block;
+ max-height: 0;
+ overflow: hidden;
+ transition: max-height 200ms ease 0s;
+ }
+ we-toggler {
+ @include we-icon-button('\f104');
+
+ &.active {
+ @include we-icon-button('\f107');
+
+ + we-collapse {
+ max-height: 500px;
+ }
+ }
+ }
+
+ .dropdown-menu {
+ // FIXME temporary fix for m2o option for example
+ position: static !important;
+ }
+ }
+
+ we-customizeblock-options.o_we_closed {
+ cursor: pointer;
+
+ > we-title {
+ @include we-icon-button('\f104');
+ }
+ > we-customizeblock-option {
+ max-height: 0;
+ transition-duration: 25ms;
+
+ + we-customizeblock-option {
+ margin-top: 0;
+ }
+ }
+ }
}
}
.oe_snippet {
// No root because can be drag and drop (and the helper is in the body)
position: relative;
+ z-index: $o-we-zindex;
width: 100px;
- background-color: $o-wysiwyg-bg-color;
- z-index: $o-wysiwyg-zindex;
+ background-color: var(--o-we-snippets-bg-color);
+ color: var(--o-we-snippets-color);
> :not(.oe_snippet_thumbnail) {
display: none !important;
@@ -178,7 +298,7 @@ body.editor_enable.editor_has_snippets {
.oe_snippet_thumbnail_img {
width: 100%;
height: 0;
- border: 1px solid $o-wysiwyg-border-color;
+ border: $o-we-border-width solid $o-we-border-color;
padding-bottom: 75%;
background-size: cover;
background-position: center center;
@@ -193,16 +313,14 @@ body.editor_enable.editor_has_snippets {
.oe_snippet_thumbnail_title {
display: block;
- border: 1px solid $o-wysiwyg-bg-color;
padding: 5px;
font-size: 12px;
font-weight: 300;
text-shadow: none;
- color: $o-wysiwyg-color;
}
&:hover .oe_snippet_thumbnail_title {
- color: $o-wysiwyg-color-light;
+ color: var(--o-we-color-accent);
}
}
}
@@ -212,66 +330,59 @@ body.editor_enable.editor_has_snippets {
.o_colorpicker_sections {
font-size: 14px;
- .o_colorpicker_section_tabs {
- width: 225px;
- height: 100%;
- }
-
button {
float: left;
- height: 25px;
- width: 25px;
- margin: 1px 1px 0 2px;
- border: 1px solid black;
+ width: percentage(1 / 8);
+ padding-top: percentage(1 / 8);
+ margin: 0;
+ border: none;
+ box-shadow: inset 0 0 0 1px var(--o-we-snippets-bg-color);
}
- div[data-group="transparent_grayscale"] button {
- @include o-alpha-button-preview;
-
- &.o_small {
- width: 23px;
- height: 23px;
- margin: 2px;
- border-radius: 50%;
- }
+ .o_colorpicker_section {
- &:hover,
- &.selected {
- box-shadow: 0px 0px 2px 2px $o-wysiwyg-color;
+ > :first-child {
+ margin: 8px 0 4px;
}
+ button {
+ position: relative;
- &.selected:before {
- content: "\f00c";
- font-family: "FontAwesome";
- padding-top: 1px;
- color: theme-color('success');
- }
+ &.o_small {
+ border-radius: 50%;
+ }
- &[data-event="foreColor"] {
- background-color: $o-wysiwyg-bg-color;
+ &:hover,
+ &.selected {
+ box-shadow: inset 0 0 1px 1px var(--o-we-snippets-bg-color);
+ }
- &:before {
- background-color: rgba(white, 0.3);
+ &.selected::before {
+ @include o-position-absolute(0, 0, 0, 0);
+ content: "\f00c" !important;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ font-family: FontAwesome !important;
+ color: $o-we-color-success;
}
- }
- &.o_btn_transparent::before {
- background-color: transparent;
+ &.o_btn_transparent::before {
+ background-color: transparent;
+ }
}
- }
- .note-color-palette>div {
- clear: both;
- }
+ &[data-name="transparent_grayscale"] button {
+ @include o-alpha-button-preview;
- .note-color-row {
- height: 28px;
+ &::before, &::after {
+ box-shadow: inherit;
+ }
+ }
}
}
.note-palette-title {
- padding: 10px 0px 10px 10px;
- color: $o-wysiwyg-color-light;
+ padding: 10px 0 10px 10px;
font-weight: bold;
}
@@ -282,14 +393,14 @@ body.editor_enable.editor_has_snippets {
.note-color-reset {
font-size: 20px !important;
- color: desaturate(rgba($o-wysiwyg-color-danger, 0.6), 40%);
+ color: desaturate(rgba($o-we-color-danger, 0.6), 40%);
margin: 1px 0 0;
padding: 0;
cursor: pointer;
&:hover {
background: transparent !important;
- color: $o-wysiwyg-color-danger;
+ color: $o-we-color-danger;
}
}
}
@@ -298,50 +409,26 @@ body.editor_enable.editor_has_snippets {
// DROPZONES
@keyframes dropZoneInsert {
to {
- background-color: rgba($o-brand-odoo, 0.2);
+ background-color: rgba($o-brand-odoo, 0.3);
}
}
-.note-editable .oe_drop_zone {
- background-color: rgba($o-brand-odoo, 0.05);
+.oe_drop_zone {
+ background-color: rgba($o-brand-odoo, 0.15);
animation: dropZoneInsert 1s linear 0s infinite alternate;
&.oe_insert {
- z-index: $o-wysiwyg-zindex - 1;
-
- border: 2px dashed $o-wysiwyg-border-color;
- border-top: none;
- border-bottom: none;
-
position: relative;
+ z-index: $o-we-overlay-zindex;
width: 100%;
- height: $o-wysiwyg-dropzone-size;
- margin: (-$o-wysiwyg-dropzone-size/2) 0;
-
- &:after {
- content: "";
- display: block;
- width: 100%;
- height: 50%;
- border-bottom: 2px dashed $o-wysiwyg-border-color;
- box-sizing: content-box;
- }
+ height: $o-we-dropzone-size;
+ margin: (-$o-we-dropzone-size/2) 0;
+ border: 2px dashed $o-we-border-color;
&.oe_vertical {
- border: 2px dashed $o-brand-odoo;
- border-left: none;
- border-right: none;
-
- width: $o-wysiwyg-dropzone-size;
+ width: $o-we-dropzone-size;
float: left;
- margin: 0 (-$o-wysiwyg-dropzone-size/2);
-
- &:after {
- width: 50%;
- height: 100%;
- border-bottom: none;
- border-right: 2px dashed $o-wysiwyg-border-color;
- }
+ margin: 0 (-$o-we-dropzone-size/2);
}
}
}
@@ -349,13 +436,14 @@ body.editor_enable.editor_has_snippets {
// MANIPULATORS
#oe_manipulators {
position: relative;
- z-index: 1;
+ z-index: $o-we-overlay-zindex;
// SNIPPET MANIPULATORS
.oe_overlay {
@include o-position-absolute;
display: none;
height: 0;
+ border-color: $o-we-handles-accent-color-preview;
background: transparent;
text-align: center;
transition: opacity 400ms linear 0s;
@@ -363,381 +451,82 @@ body.editor_enable.editor_has_snippets {
&.o_keypress {
opacity: 0;
}
-
&.oe_active {
display: block;
}
-
- // OVERLAY OPTIONS
- >.oe_overlay_options {
- font-family: $o-wysiwyg-font-family;
- @include o-position-absolute($bottom: 0, $left: 0);
- width: 250px; // the parent oe_overlay has a width equal to the snippet width, the 250px is there to force the options to stay on one line without overriding bootstrap .btn-group
- text-align: left;
- z-index: $o-wysiwyg-zindex + 1;
-
- >.btn-group {
- white-space: nowrap;
- border: 0;
-
- .btn {
- display: inline-block;
- width: $o-wysiwyg-overlay-option-size;
- height: $o-wysiwyg-overlay-option-size;
- padding: 0;
- border: 1px solid $o-wysiwyg-bg-color;
- line-height: $o-wysiwyg-overlay-option-size;
- font-size: 11px;
- border-radius: 0;
- font-weight: normal;
-
- transition: all 400ms ease 0s;
- color: $o-wysiwyg-color;
- background-color: $o-wysiwyg-bg-color;
-
- &:hover {
- color: $o-wysiwyg-color-light;
- background-color: lighten($o-wysiwyg-bg-color, 10%);
- }
-
- &.oe_snippet_remove {
- color: $o-wysiwyg-color;
- background-color: $o-wysiwyg-color-danger;
- border-color: darken($o-wysiwyg-color-danger, 20%);
-
- >.fa {
- color: $o-wysiwyg-color;
- }
-
- &:hover {
- background-color: darken($o-wysiwyg-color-danger, 20%);
- }
- }
-
- &.oe_snippet_parent>i {
- transform: scaleX(-1);
- font-size: 12px;
- }
-
- >.fa {
- color: $o-wysiwyg-color;
- line-height: inherit;
- vertical-align: top;
- }
- }
-
- // CUSTOMIZE MENU BUTTON
- >.oe_options {
- .btn {
- width: auto;
- padding: 0 25px 0 5px;
- background-color: $o-wysiwyg-bg-color;
- text-transform: uppercase;
-
- &:hover,
- &:active,
- &:focus {
- background-color: lighten($o-wysiwyg-bg-color, 5%);
- border-color: lighten($o-wysiwyg-bg-color, 10%);
- }
-
- &:before,
- &:after {
- content: "";
- width: 7px;
- height: 2px;
- background-color: $o-wysiwyg-color-light;
- @include o-position-absolute(46%);
- transition: all 0.3s ease 0s;
- }
-
- &:before {
- right: 9px;
- transform: rotate(45deg);
- }
-
- &:after {
- right: 5px;
- transform: rotate(-45deg);
- }
- }
-
- // Open menu
- &.show .btn {
-
- &:active,
- &:focus,
- &:active:focus {
- box-shadow: none;
- }
-
- &:before,
- &:after {
- width: 9px;
- }
-
- &:before {
- right: 10px;
- transform: translateX(5px) rotate(-45deg);
- }
-
- &:after {
- transform: rotate(45deg);
- }
- }
-
- // CUSTOMIZE MENU
- .dropdown-menu {
- margin: -1px 0 0 0;
- padding: 0;
- border: 1px solid $o-wysiwyg-bg-color;
- border-radius: 0;
- background-color: $o-wysiwyg-bg-color;
-
- .dropdown-item {
- position: relative;
- color: $o-wysiwyg-color-light;
- font-weight: normal;
- font-size: 12px;
- padding: 5px 40px 5px 10px;
-
- &:hover {
- background: transparent;
- cursor: pointer;
- }
-
- &.active {
- &:before {
- @include o-position-absolute($top: 5px, $right: 5px);
- content: "\f00c";
- font-family: "FontAwesome";
- color: $o-brand-primary;
- }
- }
-
- >i {
- width: 15px;
- margin-right: 10px;
- text-align: center;
- }
- }
-
- .dropdown-submenu {
- position: relative;
-
- &::before {
- @include o-position-absolute($top: 12px, $right: 10px);
- @include o-caret-right(4px);
- border-left-color: $o-wysiwyg-color-light;
- }
-
- &:hover>.dropdown-menu {
- display: block;
- left: 100%;
- top: 0;
-
- &.o_open_to_left {
- left: auto;
- right: 100%;
- }
- }
- }
-
- .dropdown-divider {
- display: block;
- height: 1px;
- border-top: 1px solid #666;
- margin: 0;
- }
-
- .dropdown-header {
- color: $o-wysiwyg-color-light;
- font-size: 11px;
- margin-top: 2px;
- text-transform: uppercase;
-
- &:hover {
- background-color: initial;
- }
-
- &.o_main_header {
- padding-left: 10px;
- }
-
- &.o_parent_editor_header {
- position: relative;
- color: $o-wysiwyg-color-light;
- font-weight: 400;
- font-size: 10px;
- margin: 10px 0 0 10px;
- text-transform: uppercase;
- padding-bottom: .25em;
-
- &::before {
- content: "\f148";
- @include o-position-absolute($top: 5px, $left: 4px);
- font-family: FontAwesome;
- transform: scaleX(-1);
- }
-
- ~.dropdown-submenu .dropdown-item,
- ~.dropdown-item {
- font-size: 11px;
- opacity: 0.7;
-
- &:hover {
- opacity: 1;
- }
- }
-
- ~.dropdown-submenu::before {
- @include o-position-absolute($top: 10px, $right: 10px);
- }
- }
- }
- }
- }
- }
- }
-
- &.o_top_cover>.oe_overlay_options {
- bottom: auto;
- top: 1px;
- right: 1px;
+ &.o_we_options_opened {
+ z-index: 1;
}
// HANDLES
- >.o_handles {
- @include o-position-absolute(-$o-wysiwyg-handles-offset-to-hide, 0, auto, 0);
- z-index: $o-wysiwyg-zindex;
-
- &:hover>.o_handle {
- background-color: rgba($o-wysiwyg-handles-accent-color, 0.05);
- }
-
- >.o_handle {
+ > .o_handles {
+ @include o-position-absolute(-$o-we-handles-offset-to-hide, 0, auto, 0);
+ border-color: inherit;
+ > .o_handle, > .o_move_handle {
position: relative;
- border: 0 solid $o-wysiwyg-handles-accent-color;
- transition: background 300ms ease 0s;
-
- &:hover,
- &.o_active {
- background-color: rgba($o-wysiwyg-handles-accent-color, 0.2);
-
- &:before {
- content: '';
- @include o-position-absolute(0, $left: 50%);
- width: 1px;
- height: 100%;
- margin-left: -1px;
- background-color: rgba($o-wysiwyg-handles-accent-color, 0.5);
- }
-
- &.w:before {
- @include o-position-absolute(50%, $left: 0);
- width: 100%;
- height: 1px;
- }
-
- &:after {
- border-color: darken($o-wysiwyg-handles-accent-color, 10%);
- background-color: darken($o-wysiwyg-handles-accent-color, 20%);
- }
- }
- &:after {
- display: block;
- width: $o-wysiwyg-handles-btn-size;
- height: $o-wysiwyg-handles-btn-size;
- border: solid 1px darken($o-wysiwyg-handles-accent-color, 10%);
- line-height: $o-wysiwyg-handles-btn-size - 2;
- font-size: 14px;
- font-family: FontAwesome;
- background-color: darken($o-wysiwyg-handles-accent-color, 20%);
- color: white;
+ &.w {
+ @include o-position-absolute($o-we-handles-offset-to-hide, auto, -$o-we-handles-offset-to-hide, 0);
+ width: $o-we-handle-edge-size;
}
- &.w:after,
- &.e:after {
- content: "\f07e";
+ &.e {
+ @include o-position-absolute($o-we-handles-offset-to-hide, 0, -$o-we-handles-offset-to-hide, auto);
+ width: $o-we-handle-edge-size;
}
- &.s:after,
- &.n:after {
- content: "\f07d";
+ &.n {
+ @include o-position-absolute($o-we-handles-offset-to-hide, 0, auto, 0);
+ height: $o-we-handle-edge-size;
}
- &.o_handle_start {
-
- &.w:after,
- &.e:after {
- content: '\f061';
- }
-
- &.n:after,
- &.s:after {
- content: '\f063';
- }
+ &.s {
+ @include o-position-absolute(auto, 0, -$o-we-handles-offset-to-hide, 0);
+ height: $o-we-handle-edge-size;
}
+ }
- &.o_handle_end {
-
- &.w:after,
- &.e:after {
- content: '\f060';
- }
-
- &.n:after,
- &.s:after {
- content: '\f062';
- }
- }
+ > .o_handle {
+ border: 0 dashed transparent;
+ border-color: inherit;
+ transition: background 300ms ease 0s;
&.w {
- @include o-position-absolute($o-wysiwyg-handles-offset-to-hide, auto, -$o-wysiwyg-handles-offset-to-hide, 0);
- width: $o-wysiwyg-handle-edge-size;
- border-width: $o-wysiwyg-handle-border-width;
+ border-width: $o-we-handle-border-width-preview;
border-right-width: 0;
cursor: e-resize;
&:after {
@include o-position-absolute($top: 50%, $left: 40%);
- margin-top: -$o-wysiwyg-handles-btn-size/2;
+ margin-top: -$o-we-handles-btn-size/2;
}
}
-
&.e {
- @include o-position-absolute($o-wysiwyg-handles-offset-to-hide, 0, -$o-wysiwyg-handles-offset-to-hide, auto);
- width: $o-wysiwyg-handle-edge-size;
- border-right-width: $o-wysiwyg-handle-border-width;
+ border-right-width: $o-we-handle-border-width-preview;
cursor: w-resize;
&:after {
@include o-position-absolute($top: 50%, $right: 40%);
- margin-top: -$o-wysiwyg-handles-btn-size/2;
+ margin-top: -$o-we-handles-btn-size/2;
}
}
-
&.n {
- @include o-position-absolute($o-wysiwyg-handles-offset-to-hide, 0, auto, 0);
- border-top-width: $o-wysiwyg-handle-border-width;
+ border-top-width: $o-we-handle-border-width-preview;
cursor: ns-resize;
&:after {
@include o-position-absolute($left: 50%, $top: 40%);
- margin-left: -$o-wysiwyg-handles-btn-size/2;
+ margin-left: -$o-we-handles-btn-size/2;
}
}
-
&.s {
- @include o-position-absolute(auto, 0, -$o-wysiwyg-handles-offset-to-hide, 0);
- border-bottom-width: $o-wysiwyg-handle-border-width;
+ border-bottom-width: $o-we-handle-border-width-preview;
cursor: ns-resize;
&:after {
@include o-position-absolute($left: 50%, $bottom: 40%);
- margin-left: -$o-wysiwyg-handles-btn-size/2;
+ margin-left: -$o-we-handles-btn-size/2;
}
}
@@ -753,6 +542,113 @@ body.editor_enable.editor_has_snippets {
}
}
}
+
+ > .o_move_handle {
+ cursor: pointer;
+ cursor: move;
+ }
+ }
+
+ &.o_we_sticky {
+ border-color: $o-we-handles-accent-color;
+
+ > .o_handles {
+ &:hover > .o_handle {
+ background-color: rgba($o-we-handles-accent-color, 0.05);
+ }
+ > .o_handle {
+ &:hover,
+ &.o_active {
+ background-color: rgba($o-we-handles-accent-color, 0.2);
+
+ &:before {
+ content: '';
+ @include o-position-absolute(0, $left: 50%);
+ width: 1px;
+ height: 100%;
+ margin-left: -1px;
+ background-color: rgba($o-we-handles-accent-color, 0.5);
+ }
+
+ &.w:before {
+ @include o-position-absolute(50%, $left: 0);
+ width: 100%;
+ height: 1px;
+ }
+
+ &:after {
+ border-color: darken($o-we-handles-accent-color, 10%);
+ background-color: darken($o-we-handles-accent-color, 20%);
+ }
+ }
+
+ &:after {
+ z-index: 1;
+ display: block;
+ width: $o-we-handles-btn-size;
+ height: $o-we-handles-btn-size;
+ border: solid 1px darken($o-we-handles-accent-color, 10%);
+ line-height: $o-we-handles-btn-size - 2;
+ font-size: 14px;
+ font-family: FontAwesome;
+ background-color: darken($o-we-handles-accent-color, 20%);
+ color: white;
+ }
+
+ &.w:after,
+ &.e:after {
+ content: "\f07e";
+ }
+
+ &.s:after,
+ &.n:after {
+ content: "\f07d";
+ }
+
+ &.o_handle_start {
+
+ &.w:after,
+ &.e:after {
+ content: '\f061';
+ }
+
+ &.n:after,
+ &.s:after {
+ content: '\f063';
+ }
+ }
+
+ &.o_handle_end {
+
+ &.w:after,
+ &.e:after {
+ content: '\f060';
+ }
+
+ &.n:after,
+ &.s:after {
+ content: '\f062';
+ }
+ }
+
+ &.w {
+ border-width: $o-we-handle-border-width;
+ border-right-width: 0;
+ }
+ &.e {
+ border-right-width: $o-we-handle-border-width;
+ }
+ &.n {
+ border-top-width: $o-we-handle-border-width;
+ }
+ &.s {
+ border-bottom-width: $o-we-handle-border-width;
+ }
+ }
+ }
+ }
+ &.o_we_options_opened > .o_handles > .o_handle {
+ border-style: solid;
}
}
diff --git a/addons/web_editor/static/src/xml/snippets.xml b/addons/web_editor/static/src/xml/snippets.xml
index 6e2ae656f2d..89819b44396 100644
--- a/addons/web_editor/static/src/xml/snippets.xml
+++ b/addons/web_editor/static/src/xml/snippets.xml
@@ -3,29 +3,41 @@
<t t-name="web_editor.snippet_overlay">
<div class="oe_overlay">
<div class="o_handles">
+ <!-- Visible overlay borders + allow to resize when not readonly -->
<div class="o_handle n readonly"/>
<div class="o_handle e readonly"/>
<div class="o_handle w readonly"/>
<div class="o_handle s readonly"/>
- </div>
- <div class="oe_overlay_options" contentEditable="false">
- <div class="btn-group">
- <a role="button" href="#" class="btn btn-secondary oe_snippet_parent" title="Select Parent Container" aria-label="Select Parent Container"><i class="fa fa-lg fa-level-up"/></a>
- <div class="dropdown oe_options d-none btn-group">
- <a role="button" href="#" data-toggle="dropdown" class="btn btn-primary" title="Customize">Customize</a>
- <div class="dropdown-menu" role="menu"/>
- </div>
- <a role="button" href="#" class="btn btn-secondary oe_snippet_move" title="Drag to Move" aria-label="Drag to Move"><i class="fa fa-arrows ml4 mr4"></i></a>
- <a role="button" href="#" class="btn btn-secondary oe_snippet_clone" title="Duplicate Container" aria-label="Duplicate Container"><i class="fa fa-files-o ml4 mr4"></i></a>
- <a role="button" href="#" class="btn btn-secondary oe_snippet_remove" title="Remove Block" aria-label="Remove Block"><i class="fa fa-trash-o ml4 mr4"></i></a>
- </div>
+
+ <!-- Invisible overlay borders which allow to drag and drop -->
+ <div class="o_move_handle n"/>
+ <div class="o_move_handle e"/>
+ <div class="o_move_handle w"/>
+ <div class="o_move_handle s"/>
</div>
</div>
</t>
+ <t t-name="web_editor.customize_block_options_section">
+ <we-customizeblock-options>
+ <we-title>
+ <span t-esc="name"/>
+ <we-button-group>
+ <we-button class="fa fa-files-o oe_snippet_clone"
+ title="Duplicate Container"
+ aria-label="Duplicate Container"/>
+ <we-button class="fa fa-trash-o oe_snippet_remove"
+ title="Remove Block"
+ aria-label="Remove Block"/>
+ </we-button-group>
+ </we-title>
+ </we-customizeblock-options>
+ </t>
+ <t t-name="web_editor.customize_block_option">
+ <we-customizeblock-option/>
+ </t>
<!-- options -->
<div t-name="web_editor.snippet.option.colorpicker" class="colorpicker">
- <div class="note-palette-title">Background Color</div>
<div class="btn-group palette-reset">
<div class="note-color-reset" data-event="foreColor" data-value="inherit" title="None" role="img" aria-label="No color">
<i class="fa fa-ban"/>