diff --git a/addons/web/static/src/js/core/smooth_scroll_on_drag.js b/addons/web/static/src/js/core/smooth_scroll_on_drag.js
new file mode 100644
index 00000000000..0d40b2e5fd8
--- /dev/null
+++ b/addons/web/static/src/js/core/smooth_scroll_on_drag.js
@@ -0,0 +1,361 @@
+odoo.define('web/static/src/js/core/smooth_scroll_on_drag.js', function (require) {
+"use strict";
+
+const Class = require('web.Class');
+const mixins = require('web.mixins');
+
+/**
+ * Provides a helper for SmoothScrollOnDrag options.offsetElements
+ */
+const OffsetElementsHelper = Class.extend({
+
+ /**
+ * @constructor
+ * @param {Object} offsetElements
+ * @param {jQuery} [offsetElements.$top] top offset element
+ * @param {jQuery} [offsetElements.$right] right offset element
+ * @param {jQuery} [offsetElements.$bottom] bottom offset element
+ * @param {jQuery} [offsetElements.$left] left offset element
+ */
+ init: function (offsetElements) {
+ this.offsetElements = offsetElements;
+ },
+ top: function () {
+ if (!this.offsetElements.$top) {
+ return 0;
+ }
+ return this.offsetElements.$top.get(0).getBoundingClientRect().bottom;
+ },
+ right: function () {
+ if (!this.offsetElements.$right) {
+ return 0;
+ }
+ return this.offsetElements.$right.get(0).getBoundingClientRect().left;
+ },
+ bottom: function () {
+ if (!this.offsetElements.$bottom) {
+ return 0;
+ }
+ return this.offsetElements.$bottom.get(0).getBoundingClientRect().top;
+ },
+ left: function () {
+ if (!this.offsetElements.$left) {
+ return 0;
+ }
+ return this.offsetElements.$left.get(0).getBoundingClientRect().right;
+ },
+});
+
+/**
+ * Provides smooth scroll behaviour on drag.
+ */
+const SmoothScrollOnDrag = Class.extend(mixins.ParentedMixin, {
+
+ /**
+ * @constructor
+ * @param {Object} parent The parent widget that uses this class.
+ * @param {jQuery} $element The element the smooth scroll on drag has to be set on.
+ * @param {jQuery} $scrollTarget The element the scroll will be triggered on.
+ * @param {Object} [options={}]
+ * @param {Object} [options.jQueryDraggableOptions={}] The configuration to be passed to
+ * the jQuery draggable function (all will be passed except scroll which will
+ * be overridden to false).
+ * @param {Number} [options.scrollOffsetThreshold=150] (Integer) The distance from the
+ * bottom/top of the options.$scrollTarget from which the smooth scroll will be
+ * triggered.
+ * @param {Number} [options.scrollStep=20] (Integer) The step of the scroll.
+ * @param {Number} [options.scrollTimerInterval=5] (Integer) The interval (in ms) the
+ * scrollStep will be applied.
+ * @param {Object} [options.offsetElements={}] Visible elements in $scrollTarget that
+ * reduce $scrollTarget drag visible area (scroll will be triggered sooner that
+ * normally). A selector is passed so that elements such automatically hidden
+ * menu can then be correctly handled.
+ * @param {jQuery} [options.offsetElements.$top={}] Visible top element which height will
+ * be taken into account when triggering scroll at the top of the $scrollTarget.
+ * @param {jQuery} [options.offsetElements.$right={}] Visible right element which width
+ * will be taken into account when triggering scroll at the right side of the
+ * $scrollTarget.
+ * @param {jQuery} [options.offsetElements.$bottom={}] Visible bottom element which height
+ * will be taken into account when triggering scroll at bottom of the $scrollTarget.
+ * @param {jQuery} [options.offsetElements.$left={}] Visible right element which width
+ * will be taken into account when triggering scroll at the left side of the
+ * $scrollTarget.
+ */
+ init(parent, $element, $scrollTarget, options = {}) {
+ mixins.ParentedMixin.init.call(this);
+ this.setParent(parent);
+
+ this.$element = $element;
+ this.$scrollTarget = $scrollTarget;
+ this.options = options;
+
+ // Setting optional options to their default value if not provided
+ this.options.jQueryDraggableOptions = this.options.jQueryDraggableOptions || {};
+ if (!this.options.jQueryDraggableOptions.cursorAt) {
+ this.$element.on('mousedown.smooth_scroll', this._onElementMouseDown.bind(this));
+ }
+ this.options.scrollOffsetThreshold = this.options.scrollOffsetThreshold || 150;
+ this.options.scrollStep = this.options.scrollStep || 20;
+ this.options.scrollTimerInterval = this.options.scrollTimerInterval || 5;
+ this.options.offsetElements = this.options.offsetElements || {};
+ this.options.offsetElementsManager = new OffsetElementsHelper(this.options.offsetElements);
+
+ this.autoScrollHandler = null;
+
+ this.scrollStepDirectionEnum = {
+ up: -1,
+ right: 1,
+ down: 1,
+ left: -1,
+ };
+
+ this.options.jQueryDraggableOptions.scroll = false;
+ const draggableOptions = Object.assign({}, this.options.jQueryDraggableOptions, {
+ start: (ev, ui) => this._onSmoothDragStart(ev, ui, this.options.jQueryDraggableOptions.start),
+ drag: (ev, ui) => this._onSmoothDrag(ev, ui, this.options.jQueryDraggableOptions.drag),
+ stop: (ev, ui) => this._onSmoothDragStop(ev, ui, this.options.jQueryDraggableOptions.stop),
+ });
+ this.$element.draggable(draggableOptions);
+ },
+ /**
+ * @override
+ */
+ destroy: function () {
+ mixins.ParentedMixin.destroy.call(this);
+ this.$element.off('.smooth_scroll');
+ this._stopSmoothScroll();
+ },
+
+ //--------------------------------------------------------------------------
+ // Private
+ //--------------------------------------------------------------------------
+
+ /**
+ * Starts the scroll process using the options.
+ * The options will be updated dynamically when the handler _onSmoothDrag
+ * will be called. The interval will be cleared when the handler
+ * _onSmoothDragStop will be called.
+ *
+ * @private
+ * @param {Object} ui The jQuery drag handler ui parameter.
+ */
+ _startSmoothScroll(ui) {
+ this._stopSmoothScroll();
+ this.autoScrollHandler = setInterval(
+ () => {
+ // Prevents Delta's from being different from 0 when scroll should not occur (except when
+ // helper is dragged outside of this.$scrollTarget's visible area as it increases
+ // this.$scrollTarget's scrollHeight).
+ // Also, this code prevents the helper from being incorrectly repositioned when target is
+ // a child of this.$scrollTarget.
+ this.verticalDelta = Math.min(
+ // Ensures scrolling stops when dragging bellow this.$scrollTarget bottom.
+ Math.max(
+ 0,
+ this.$scrollTarget.get(0).scrollHeight
+ - (this.$scrollTarget.scrollTop() + this.$scrollTarget.innerHeight())
+ ),
+ // Ensures scrolling stops when dragging above this.$scrollTarget top.
+ Math.max(
+ this.verticalDelta,
+ -this.$scrollTarget.scrollTop()
+ )
+ );
+ this.horizontalDelta = Math.min(
+ //Ensures scrolling stops when dragging left to this.$scrollTarget.
+ Math.max(
+ 0,
+ this.$scrollTarget.get(0).scrollWidth
+ - (this.$scrollTarget.scrollLeft() + this.$scrollTarget.innerWidth())
+ ),
+ //Ensures scrolling stops when dragging right to this.$scrollTarget.
+ Math.max(
+ this.horizontalDelta,
+ -this.$scrollTarget.scrollLeft()
+ )
+ );
+
+ // Keep helper at right position while scrolling when helper is a child of this.$scrollTarget.
+ if (this.scrollTargetIsParent) {
+ const offset = ui.helper.offset();
+ ui.helper.offset({
+ top: offset.top + this.verticalDelta,
+ left: offset.left + this.horizontalDelta
+ });
+ }
+ console.log(this.verticalDelta);
+ this.$scrollTarget.scrollTop(
+ this.$scrollTarget.scrollTop() +
+ this.verticalDelta
+ );
+ this.$scrollTarget.scrollLeft(
+ this.$scrollTarget.scrollLeft() +
+ this.horizontalDelta
+ );
+ },
+ this.options.scrollTimerInterval
+ );
+ },
+ /**
+ * Stops the scroll process if any is running.
+ *
+ * @private
+ */
+ _stopSmoothScroll() {
+ clearInterval(this.autoScrollHandler);
+ },
+ /**
+ * Updates the options depending on the offset position of the draggable
+ * helper. In the same time options are used by an interval to trigger
+ * scroll behaviour.
+ * @see {@link _startSmoothScroll} for interval implementation details.
+ *
+ * @private
+ * @param {Object} ui The jQuery drag handler ui parameter.
+ */
+ _updatePositionOptions(ui) {
+ const draggableHelperOffset = ui.offset;
+ const scrollTargetOffset = this.$scrollTarget.offset();
+ let visibleOffset = {
+ top: draggableHelperOffset.top
+ - scrollTargetOffset.top
+ + this.options.jQueryDraggableOptions.cursorAt.top
+ - this.options.offsetElementsManager.top(),
+ right: scrollTargetOffset.left + this.$scrollTarget.outerWidth()
+ - draggableHelperOffset.left
+ - this.options.jQueryDraggableOptions.cursorAt.left
+ - this.options.offsetElementsManager.right(),
+ bottom: scrollTargetOffset.top + this.$scrollTarget.outerHeight()
+ - draggableHelperOffset.top
+ - this.options.jQueryDraggableOptions.cursorAt.top
+ - this.options.offsetElementsManager.bottom(),
+ left: draggableHelperOffset.left
+ - scrollTargetOffset.left
+ + this.options.jQueryDraggableOptions.cursorAt.left
+ - this.options.offsetElementsManager.left(),
+ };
+
+ // If this.$scrollTarget is the html tag, we need to take the scroll position in to account
+ // as offsets positions are calculated relative to the document (thus ).
+ if (this.scrollTargetIsDocument) {
+ const scrollTargetScrollTop = this.$scrollTarget.scrollTop();
+ const scrollTargetScrollLeft = this.$scrollTarget.scrollLeft();
+ visibleOffset.top -= scrollTargetScrollTop;
+ visibleOffset.right += scrollTargetScrollLeft;
+ visibleOffset.bottom += scrollTargetScrollTop;
+ visibleOffset.left -= scrollTargetScrollLeft;
+ }
+
+ const scrollDecelerator = {
+ vertical: 0,
+ horizontal: 0,
+ };
+
+ const scrollStepDirection = {
+ vertical: this.scrollStepDirectionEnum.down,
+ horizontal: this.scrollStepDirectionEnum.right,
+ };
+
+ // Manage vertical scroll
+ if (visibleOffset.bottom <= this.options.scrollOffsetThreshold) {
+ scrollDecelerator.vertical = Math.max(0, visibleOffset.bottom)
+ / this.options.scrollOffsetThreshold;
+ } else if (visibleOffset.top <= this.options.scrollOffsetThreshold) {
+ scrollDecelerator.vertical = Math.max(0, visibleOffset.top)
+ / this.options.scrollOffsetThreshold;
+ scrollStepDirection.vertical = this.scrollStepDirectionEnum.up;
+ } else {
+ scrollDecelerator.vertical = 1;
+ }
+
+ // Manage horizontal scroll
+ if (visibleOffset.right <= this.options.scrollOffsetThreshold) {
+ scrollDecelerator.horizontal = Math.max(0, visibleOffset.right)
+ / this.options.scrollOffsetThreshold;
+ } else if (visibleOffset.left <= this.options.scrollOffsetThreshold) {
+ scrollDecelerator.horizontal = Math.max(0, visibleOffset.left)
+ / this.options.scrollOffsetThreshold;
+ scrollStepDirection.horizontal = this.scrollStepDirectionEnum.left;
+ } else {
+ scrollDecelerator.horizontal = 1;
+ }
+
+ this.verticalDelta = Math.ceil(scrollStepDirection.vertical *
+ this.options.scrollStep *
+ (1 - Math.sqrt(scrollDecelerator.vertical)));
+ this.horizontalDelta = Math.ceil(scrollStepDirection.horizontal *
+ this.options.scrollStep *
+ (1 - Math.sqrt(scrollDecelerator.horizontal)));
+ },
+
+ //--------------------------------------------------------------------------
+ // Handlers
+ //--------------------------------------------------------------------------
+
+ /**
+ * Called when mouse button is down on this.$element.
+ * Updates the mouse cursor position variable.
+ *
+ * @private
+ * @param {Object} ev The jQuery mousedown handler event parameter.
+ */
+ _onElementMouseDown(ev) {
+ const elementOffset = $(ev.target).offset();
+ this.options.jQueryDraggableOptions.cursorAt = {
+ top: ev.pageY - elementOffset.top,
+ left: ev.pageX - elementOffset.left,
+ };
+ },
+ /**
+ * Called when dragging the element.
+ * Updates the position options and call the provided callback if any.
+ *
+ * @private
+ * @param {Object} ev The jQuery drag handler event parameter.
+ * @param {Object} ui The jQuery drag handler ui parameter.
+ * @param {Function} onDragCallback The jQuery drag callback.
+ */
+ _onSmoothDrag(ev, ui, onDragCallback) {
+ this._updatePositionOptions(ui);
+ if (typeof onDragCallback === 'function') {
+ onDragCallback.call(ui.helper, ev, ui);
+ }
+ },
+ /**
+ * Called when starting to drag the element.
+ * Updates the position params, starts smooth scrolling process and call the
+ * provided callback if any.
+ *
+ * @private
+ * @param {Object} ev The jQuery drag handler event parameter.
+ * @param {Object} ui The jQuery drag handler ui parameter.
+ * @param {Function} onDragStartCallBack The jQuery drag callback.
+ */
+ _onSmoothDragStart(ev, ui, onDragStartCallBack) {
+ this.scrollTargetIsDocument = this.$scrollTarget.is('html');
+ this.scrollTargetIsParent = this.$scrollTarget.get(0).contains(this.$element.get(0));
+ this._updatePositionOptions(ui);
+ this._startSmoothScroll(ui);
+ if (typeof onDragStartCallBack === 'function') {
+ onDragStartCallBack.call(ui.helper, ev, ui);
+ }
+ },
+ /**
+ * Called when stopping to drag the element.
+ * Stops the smooth scrolling process and call the provided callback if any.
+ *
+ * @private
+ * @param {Object} ev The jQuery drag handler event parameter.
+ * @param {Object} ui The jQuery drag handler ui parameter.
+ * @param {Function} onDragEndCallBack The jQuery drag callback.
+ */
+ _onSmoothDragStop(ev, ui, onDragEndCallBack) {
+ this._stopSmoothScroll();
+ if (typeof onDragEndCallBack === 'function') {
+ onDragEndCallBack.call(ui.helper, ev, ui);
+ }
+ },
+});
+
+return SmoothScrollOnDrag;
+});
diff --git a/addons/web/views/webclient_templates.xml b/addons/web/views/webclient_templates.xml
index eaef3884128..c074328ac60 100644
--- a/addons/web/views/webclient_templates.xml
+++ b/addons/web/views/webclient_templates.xml
@@ -172,6 +172,7 @@
+
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 76b19dc62ed..d198af532b7 100644
--- a/addons/web_editor/static/src/js/editor/snippets.editor.js
+++ b/addons/web_editor/static/src/js/editor/snippets.editor.js
@@ -10,6 +10,7 @@ var Widget = require('web.Widget');
var options = require('web_editor.snippets.options');
var Wysiwyg = require('web_editor.wysiwyg');
const {ColorPaletteWidget} = require('web_editor.ColorPalette');
+const SmoothScrollOnDrag = require('web/static/src/js/core/smooth_scroll_on_drag.js');
var _t = core._t;
@@ -19,169 +20,6 @@ var globalSelector = {
is: () => false,
};
-/**
- * Component that provides smooth scroll behaviour on drag.
- *
- * Do not forget to call unsetDraggable to ensure proper resource clearing.
- * @see {@link unsetDraggable}.
- */
-const SmoothOnDragComponent = Class.extend({
- /**
- * @constructor
- * @param {jQuery} $element The element the smooth drag has to be set on
- * @param {Object} jQueryDraggableOptions The configuration to be passed to
- * the jQuery draggable function
- */
- init($element, jQueryDraggableOptions) {
- this.$element = $element;
- this.$scrollTarget = $('html');
- this.autoScrollHandler = null;
- this.cursorAt = jQueryDraggableOptions.cursorAt || {left: 0, top: 0};
- this.draggableOffset = 0;
- this.mainNavBarHeight = $('.o_main_navbar').innerHeight();
- this.scrollOffsetThreshold = 150;
- this.scrollStep = 20;
- this.scrollStepDirection = 1;
- this.scrollStepDirectionEnum = {up: -1, down: 1};
- this.scrollDecelerator = 0;
- this.scrollTimer = 5;
- this.visibleBottomOffset = 0;
- this.visibleTopOffset = 0;
-
- jQueryDraggableOptions.scroll = false;
- const draggableOptions = Object.assign({}, jQueryDraggableOptions, {
- start: (ev, ui) => this._onSmoothDragStart(ev, ui, jQueryDraggableOptions.start),
- drag: (ev, ui) => this._onSmoothDrag(ev, ui, jQueryDraggableOptions.drag),
- stop: (ev, ui) => this._onSmoothDragStop(ev, ui, jQueryDraggableOptions.stop),
- });
- this.$element.draggable(draggableOptions);
- },
-
- //--------------------------------------------------------------------------
- // Public
- //--------------------------------------------------------------------------
-
- /**
- * Ensures correct clearing of resources.
- */
- unsetDraggable() {
- this._stopSmoothScroll();
- },
-
- //--------------------------------------------------------------------------
- // Private
- //--------------------------------------------------------------------------
-
- /**
- * Starts the scroll process using the options.
- * The options will be updated dynamically when the handler _onSmoothDrag
- * will be called. The interval will be cleared when the handler
- * _onSmoothDragStop will be called.
- *
- * @private
- */
- _startSmoothScroll() {
- this._stopSmoothScroll();
- this.autoScrollHandler = setInterval(
- () => {
- this.$scrollTarget.scrollTop(
- this.$scrollTarget.scrollTop() +
- this.scrollStepDirection *
- this.scrollStep *
- (1 - this.scrollDecelerator)
- );
- },
- this.scrollTimer
- );
- },
- /**
- * Stops the scroll process if any is running.
- *
- * @private
- */
- _stopSmoothScroll() {
- clearInterval(this.autoScrollHandler);
- },
- /**
- * Updates the options depending on the offset position of the draggable
- * helper. In the same time options are used by an interval to trigger
- * scroll behaviour.
- * @see {@link _startSmoothScroll} for interval implementation details.
- *
- * @private
- * @param {number} dragTopOffset The offset position of the draggable helper
- */
- _updatePositionOptions(dragTopOffset) {
- this.draggableOffset = this.cursorAt.top + dragTopOffset;
- this.visibleBottomOffset = this.$scrollTarget.scrollTop() +
- this.$scrollTarget.get(0).clientHeight - this.draggableOffset;
- this.visibleTopOffset = this.draggableOffset - this.$scrollTarget.scrollTop();
- if (this.visibleTopOffset <= this.scrollOffsetThreshold + this.mainNavBarHeight) {
- this.scrollDecelerator = this.visibleTopOffset /
- (this.scrollOffsetThreshold + this.mainNavBarHeight);
- this.scrollStepDirection = this.scrollStepDirectionEnum.up;
- } else if (this.visibleBottomOffset <= this.scrollOffsetThreshold) {
- this.scrollDecelerator = this.visibleBottomOffset /
- this.scrollOffsetThreshold;
- this.scrollStepDirection = this.scrollStepDirectionEnum.down;
- } else {
- this.scrollDecelerator = 1;
- }
- },
-
- //--------------------------------------------------------------------------
- // Handlers
- //--------------------------------------------------------------------------
-
- /**
- * Called when dragging the element.
- * Updates the position options and call the provided callback if any.
- *
- * @private
- * @param {Object} ev The jQuery drag handler event parameter.
- * @param {Object} ui The jQuery drag handler ui parameter.
- * @param {Function} onDragCallback The jQuery drag callback.
- */
- _onSmoothDrag(ev, ui, onDragCallback) {
- this._updatePositionOptions(ui.offset.top);
- if (typeof onDragCallback === 'function') {
- onDragCallback.call(ui.helper, ev, ui);
- }
- },
- /**
- * Called when starting to drag the element.
- * Updates the position params, starts smooth scrolling process and call the
- * provided callback if any.
- *
- * @private
- * @param {Object} ev The jQuery drag handler event parameter.
- * @param {Object} ui The jQuery drag handler ui parameter.
- * @param {Function} onDragStartCallBack The jQuery drag callback.
- */
- _onSmoothDragStart(ev, ui, onDragStartCallBack) {
- this._updatePositionOptions(ui.offset.top);
- this._startSmoothScroll();
- if (typeof onDragStartCallBack === 'function') {
- onDragStartCallBack.call(ui.helper, ev, ui);
- }
- },
- /**
- * Called when stopping to drag the element.
- * Stops the smooth scrolling process and call the provided callback if any.
- *
- * @private
- * @param {Object} ev The jQuery drag handler event parameter.
- * @param {Object} ui The jQuery drag handler ui parameter.
- * @param {Function} onDragEndCallBack The jQuery drag callback.
- */
- _onSmoothDragStop(ev, ui, onDragEndCallBack) {
- this._stopSmoothScroll();
- if (typeof onDragEndCallBack === 'function') {
- onDragEndCallBack.call(ui.helper, ev, ui);
- }
- },
-});
-
/**
* Management of the overlay and option list for a snippet.
*/
@@ -235,32 +73,39 @@ var SnippetEditor = Widget.extend({
// Initialize move/clone/remove buttons
if (this.isTargetMovable) {
this.dropped = false;
- this.draggableComponent = new SmoothOnDragComponent(this.$el, {
- appendTo: this.$body,
- cursor: 'move',
- cursorAt: {
- left: 10,
- top: 10
+ const smoothScrollOptions = {
+ offsetElements: {
+ $top: $('#top'),
+ $left: $('#oe_snippets'),
},
- greedy: true,
- handle: '.o_move_handle',
- helper: () => {
- var $clone = this.$el.clone().css({width: '24px', height: '24px', border: 0});
- $clone.appendTo(this.$body).removeClass('d-none');
- return $clone;
+ jQueryDraggableOptions: {
+ appendTo: this.$body,
+ cursor: 'move',
+ cursorAt: {
+ left: 10,
+ top: 10
+ },
+ greedy: true,
+ handle: '.o_move_handle',
+ helper: () => {
+ var $clone = this.$el.clone().css({width: '24px', height: '24px', border: 0});
+ $clone.appendTo(this.$body).removeClass('d-none');
+ return $clone;
+ },
+ scroll: false,
+ start: this._onDragAndDropStart.bind(this),
+ stop: (...args) => {
+ // Delay our stop handler so that some summernote handlers
+ // which occur on mouseup (and are themself delayed) are
+ // executed first (this prevents the library to crash
+ // because our stop handler may change the DOM).
+ setTimeout(() => {
+ this._onDragAndDropStop(...args);
+ }, 0);
+ },
},
- scroll: false,
- start: this._onDragAndDropStart.bind(this),
- stop: (...args) => {
- // Delay our stop handler so that some summernote handlers
- // which occur on mouseup (and are themself delayed) are
- // executed first (this prevents the library to crash
- // because our stop handler may change the DOM).
- setTimeout(() => {
- this._onDragAndDropStop(...args);
- }, 0);
- },
- });
+ };
+ this.draggableComponent = new SmoothScrollOnDrag(this, this.$el, $('html'), smoothScrollOptions);
} else {
this.$('.o_overlay_move_options').addClass('d-none');
$customize.find('.oe_snippet_clone').addClass('d-none');
@@ -304,9 +149,6 @@ var SnippetEditor = Widget.extend({
*/
destroy: function () {
this._super(...arguments);
- if (this.draggableComponent) {
- this.draggableComponent.unsetDraggable();
- }
this.$target.removeData('snippet-editor');
this.$target.off('.snippet_editor');
},
@@ -1186,9 +1028,6 @@ var SnippetsMenu = Widget.extend({
*/
destroy: function () {
this._super.apply(this, arguments);
- if (this.draggableComponent) {
- this.draggableComponent.unsetDraggable();
- }
if (this.$window) {
this.$snippetEditorArea.remove();
this.$window.off('.snippets_menu');
@@ -1929,117 +1768,124 @@ var SnippetsMenu = Widget.extend({
var $tumb = $snippets.find('.oe_snippet_thumbnail_img:first');
var $toInsert, dropped, $snippet;
- this.draggableComponent = new SmoothOnDragComponent($snippets, {
- appendTo: this.$body,
- cursor: 'move',
- distance: 0,
- greedy: true,
- handle: '.oe_snippet_thumbnail',
- helper: function () {
- const dragSnip = this.cloneNode(true);
- dragSnip.querySelectorAll('.o_delete_btn, .o_image_ribbon').forEach(
- el => el.remove()
- );
- return dragSnip;
+ const smoothScrollOptions = {
+ offsetElements: {
+ $top: $('#top'),
+ $left: $('#oe_snippets'),
},
- scroll: false,
- start: function () {
- dropped = false;
- $snippet = $(this);
- var $baseBody = $snippet.find('.oe_snippet_body');
- var $selectorSiblings = $();
- var $selectorChildren = $();
- var temp = self.templateOptions;
- for (var k in temp) {
- if ($baseBody.is(temp[k].base_selector) && !$baseBody.is(temp[k].base_exclude)) {
- if (temp[k]['drop-near']) {
- $selectorSiblings = $selectorSiblings.add(temp[k]['drop-near'].all());
- }
- if (temp[k]['drop-in']) {
- $selectorChildren = $selectorChildren.add(temp[k]['drop-in'].all());
+ jQueryDraggableOptions: {
+ appendTo: this.$body,
+ cursor: 'move',
+ distance: 0,
+ greedy: true,
+ handle: '.oe_snippet_thumbnail',
+ helper: function () {
+ const dragSnip = this.cloneNode(true);
+ dragSnip.querySelectorAll('.o_delete_btn, .o_image_ribbon').forEach(
+ el => el.remove()
+ );
+ return dragSnip;
+ },
+ scroll: false,
+ start: function () {
+ dropped = false;
+ $snippet = $(this);
+ var $baseBody = $snippet.find('.oe_snippet_body');
+ var $selectorSiblings = $();
+ var $selectorChildren = $();
+ var temp = self.templateOptions;
+ for (var k in temp) {
+ if ($baseBody.is(temp[k].base_selector) && !$baseBody.is(temp[k].base_exclude)) {
+ if (temp[k]['drop-near']) {
+ $selectorSiblings = $selectorSiblings.add(temp[k]['drop-near'].all());
+ }
+ if (temp[k]['drop-in']) {
+ $selectorChildren = $selectorChildren.add(temp[k]['drop-in'].all());
+ }
}
}
- }
- $toInsert = $baseBody.clone();
+ $toInsert = $baseBody.clone();
- 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");
- return;
- }
-
- self._activateSnippet(false);
- self._activateInsertionZones($selectorSiblings, $selectorChildren);
-
- self.getEditableArea().find('.oe_drop_zone').droppable({
- over: function () {
- if (!dropped) {
- dropped = true;
- $(this).first().after($toInsert).addClass('d-none');
- $toInsert.removeClass('oe_snippet_body');
- }
- },
- out: function () {
- var prev = $toInsert.prev();
- if (this === prev[0]) {
- dropped = false;
- $toInsert.detach();
- $(this).removeClass('d-none');
- $toInsert.addClass('oe_snippet_body');
- }
- },
- });
- },
- stop: function (ev, ui) {
- $toInsert.removeClass('oe_snippet_body');
-
- if (!dropped && ui.position.top > 3 && ui.position.left + 50 > self.$el.outerWidth()) {
- var $el = $.nearest({x: ui.position.left, y: ui.position.top}, '.oe_drop_zone', {container: document.body}).first();
- if ($el.length) {
- $el.after($toInsert);
- dropped = true;
- }
- }
-
- self.getEditableArea().find('.oe_drop_zone').droppable('destroy').remove();
-
- if (dropped) {
- var prev = $toInsert.first()[0].previousSibling;
- var next = $toInsert.last()[0].nextSibling;
-
- if (prev) {
- $toInsert.detach();
- self.trigger_up('request_history_undo_record', {$target: $(prev)});
- $toInsert.insertAfter(prev);
- } else if (next) {
- $toInsert.detach();
- self.trigger_up('request_history_undo_record', {$target: $(next)});
- $toInsert.insertBefore(next);
- } else {
- var $parent = $toInsert.parent();
- $toInsert.detach();
- self.trigger_up('request_history_undo_record', {$target: $parent});
- $parent.prepend($toInsert);
+ 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");
+ return;
}
- var $target = $toInsert;
+ self._activateSnippet(false);
+ self._activateInsertionZones($selectorSiblings, $selectorChildren);
- _.defer(function () {
- self.trigger_up('snippet_dropped', {$target: $target});
- self._disableUndroppableSnippets();
-
- self._callForEachChildSnippet($target, function (editor, $snippet) {
- return editor.buildSnippet();
- }).then(function () {
- $target.trigger('content_changed');
- return self._updateInvisibleDOM();
- });
+ self.getEditableArea().find('.oe_drop_zone').droppable({
+ over: function () {
+ if (!dropped) {
+ dropped = true;
+ $(this).first().after($toInsert).addClass('d-none');
+ $toInsert.removeClass('oe_snippet_body');
+ }
+ },
+ out: function () {
+ var prev = $toInsert.prev();
+ if (this === prev[0]) {
+ dropped = false;
+ $toInsert.detach();
+ $(this).removeClass('d-none');
+ $toInsert.addClass('oe_snippet_body');
+ }
+ },
});
- } else {
- $toInsert.remove();
- }
+ },
+ stop: function (ev, ui) {
+ $toInsert.removeClass('oe_snippet_body');
+
+ if (!dropped && ui.position.top > 3 && ui.position.left + 50 > self.$el.outerWidth()) {
+ var $el = $.nearest({x: ui.position.left, y: ui.position.top}, '.oe_drop_zone', {container: document.body}).first();
+ if ($el.length) {
+ $el.after($toInsert);
+ dropped = true;
+ }
+ }
+
+ self.getEditableArea().find('.oe_drop_zone').droppable('destroy').remove();
+
+ if (dropped) {
+ var prev = $toInsert.first()[0].previousSibling;
+ var next = $toInsert.last()[0].nextSibling;
+
+ if (prev) {
+ $toInsert.detach();
+ self.trigger_up('request_history_undo_record', {$target: $(prev)});
+ $toInsert.insertAfter(prev);
+ } else if (next) {
+ $toInsert.detach();
+ self.trigger_up('request_history_undo_record', {$target: $(next)});
+ $toInsert.insertBefore(next);
+ } else {
+ var $parent = $toInsert.parent();
+ $toInsert.detach();
+ self.trigger_up('request_history_undo_record', {$target: $parent});
+ $parent.prepend($toInsert);
+ }
+
+ var $target = $toInsert;
+
+ _.defer(function () {
+ self.trigger_up('snippet_dropped', {$target: $target});
+ self._disableUndroppableSnippets();
+
+ self._callForEachChildSnippet($target, function (editor, $snippet) {
+ return editor.buildSnippet();
+ }).then(function () {
+ $target.trigger('content_changed');
+ return self._updateInvisibleDOM();
+ });
+ });
+ } else {
+ $toInsert.remove();
+ }
+ },
},
- });
+ };
+ this.draggableComponent = new SmoothScrollOnDrag(this, $snippets, $('html'), smoothScrollOptions);
},
/**
* Adds the 'o_default_snippet_text' class on nodes which contain only
@@ -2439,7 +2285,6 @@ var SnippetsMenu = Widget.extend({
return {
Class: SnippetsMenu,
Editor: SnippetEditor,
- SmoothOnDragComponent: SmoothOnDragComponent,
globalSelector: globalSelector,
};
});