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, }; });