diff options
| author | Laurent Stukkens (LTU) <ltu@odoo.com> | 2020-07-02 16:08:10 +0000 |
|---|---|---|
| committer | Laurent Stukkens (LTU) <ltu@odoo.com> | 2020-07-08 12:13:33 +0000 |
| commit | 6df67a28c2f113572acd4a3db19ab6b1bd37e7da (patch) | |
| tree | 91bd842dbfbed5cb96a7b706adcc9893e241ed3e | |
| parent | ec7986cba3953dc6dfe16f0b1e0bdd13dac2b347 (diff) | |
[REF] web_editor,web: reuse the autoscroll on drag feature
Prior to this commit, the autoscroll on drag feature was accessible and only
used in the web_editor application.
After this commit, the autoscroll on drag feature will be available in the
web application and has been refactored in order to allow its usage
in other application (as for the sign application).
task-2280965
closes odoo/odoo#54072
Related: odoo/enterprise#11656
Signed-off-by: Yannick Tivisse (yti) <yti@odoo.com>
| -rw-r--r-- | addons/web/static/src/js/core/smooth_scroll_on_drag.js | 361 | ||||
| -rw-r--r-- | addons/web/views/webclient_templates.xml | 1 | ||||
| -rw-r--r-- | addons/web_editor/static/src/js/editor/snippets.editor.js | 419 |
3 files changed, 494 insertions, 287 deletions
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 <html>). + 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 @@ <script type="text/javascript" src="/web/static/src/js/widgets/name_and_signature.js"></script> <script type="text/javascript" src="/web/static/src/js/widgets/notification.js"></script> <script type="text/javascript" src="/web/static/src/js/widgets/rainbow_man.js"></script> + <script type="text/javascript" src="/web/static/src/js/core/smooth_scroll_on_drag.js"></script> <script type="text/javascript" src="/web/static/src/js/widgets/colorpicker.js"></script> <script type="text/javascript" src="/web/static/src/js/widgets/translation_dialog.js"></script> <script type="text/javascript" src="/web/static/src/js/libs/zoomodoo.js"></script> 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; @@ -20,169 +21,6 @@ var globalSelector = { }; /** - * 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. */ var SnippetEditor = Widget.extend({ @@ -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 - }, - 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; + const smoothScrollOptions = { + offsetElements: { + $top: $('#top'), + $left: $('#oe_snippets'), }, - 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); + 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); + }, }, - }); + }; + 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; - } + 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._activateSnippet(false); + self._activateInsertionZones($selectorSiblings, $selectorChildren); - self.getEditableArea().find('.oe_drop_zone').droppable({ - over: function () { - if (!dropped) { + 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; - $(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(); + self.getEditableArea().find('.oe_drop_zone').droppable('destroy').remove(); - if (dropped) { - var prev = $toInsert.first()[0].previousSibling; - var next = $toInsert.last()[0].nextSibling; + 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 (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; + var $target = $toInsert; - _.defer(function () { - self.trigger_up('snippet_dropped', {$target: $target}); - self._disableUndroppableSnippets(); + _.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._callForEachChildSnippet($target, function (editor, $snippet) { + return editor.buildSnippet(); + }).then(function () { + $target.trigger('content_changed'); + return self._updateInvisibleDOM(); + }); }); - }); - } else { - $toInsert.remove(); - } + } 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, }; }); |
