diff options
Diffstat (limited to 'addons/point_of_sale/static/src/js/Misc/Draggable.js')
| -rw-r--r-- | addons/point_of_sale/static/src/js/Misc/Draggable.js | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/addons/point_of_sale/static/src/js/Misc/Draggable.js b/addons/point_of_sale/static/src/js/Misc/Draggable.js new file mode 100644 index 00000000..cbb1eba8 --- /dev/null +++ b/addons/point_of_sale/static/src/js/Misc/Draggable.js @@ -0,0 +1,142 @@ +odoo.define('point_of_sale.Draggable', function(require) { + 'use strict'; + + const { useExternalListener } = owl.hooks; + const { useListener } = require('web.custom_hooks'); + const PosComponent = require('point_of_sale.PosComponent'); + const Registries = require('point_of_sale.Registries'); + + /** + * Wrap an element or a component with { position: absolute } to make it + * draggable around the limitArea or the nearest positioned ancestor. + * + * e.g. + * ``` + * <div class="limit-area"> + * <Draggable limitArea="'.limit-area'"> + * <div class="popup"> + * <header class="drag-handle"></header> + * </div> + * <div class="popup body"></div> + * </Draggable> + * </div> + * ``` + * + * In the above snippet, if the popup div is { position: absolute }, + * then it becomes draggable around the .limit-area element if it is dragged + * thru its Header -- because of the .drag-handle element. + * + * @trigger 'drag-end' when dragging ended with payload `{ loc: { top, left } }` + */ + class Draggable extends PosComponent { + constructor() { + super(...arguments); + this.isDragging = false; + this.dx = 0; + this.dy = 0; + // drag with mouse + useExternalListener(document, 'mousemove', this.move); + useExternalListener(document, 'mouseup', this.endDrag); + // drag with touch + useExternalListener(document, 'touchmove', this.move); + useExternalListener(document, 'touchend', this.endDrag); + + useListener('mousedown', '.drag-handle', this.startDrag); + useListener('touchstart', '.drag-handle', this.startDrag); + } + mounted() { + this.limitArea = this.props.limitArea + ? document.querySelector(this.props.limitArea) + : this.el.offsetParent; + this.limitAreaBoundingRect = this.limitArea.getBoundingClientRect(); + if (this.limitArea === this.el.offsetParent) { + this.limitLeft = 0; + this.limitTop = 0; + this.limitRight = this.limitAreaBoundingRect.width; + this.limitBottom = this.limitAreaBoundingRect.height; + } else { + this.limitLeft = -this.el.offsetParent.offsetLeft; + this.limitTop = -this.el.offsetParent.offsetTop; + this.limitRight = + this.limitAreaBoundingRect.width - this.el.offsetParent.offsetLeft; + this.limitBottom = + this.limitAreaBoundingRect.height - this.el.offsetParent.offsetTop; + } + this.limitAreaWidth = this.limitAreaBoundingRect.width; + this.limitAreaHeight = this.limitAreaBoundingRect.height; + + // absolutely position the element then remove the transform. + const elBoundingRect = this.el.getBoundingClientRect(); + this.el.style.top = `${elBoundingRect.top}px`; + this.el.style.left = `${elBoundingRect.left}px`; + this.el.style.transform = 'none'; + } + startDrag(event) { + let realEvent; + if (event instanceof CustomEvent) { + realEvent = event.detail; + } else { + realEvent = event; + } + const { x, y } = this._getEventLoc(realEvent); + this.isDragging = true; + this.dx = this.el.offsetLeft - x; + this.dy = this.el.offsetTop - y; + event.stopPropagation(); + } + move(event) { + if (this.isDragging) { + const { x: pointerX, y: pointerY } = this._getEventLoc(event); + const posLeft = this._getPosLeft(pointerX, this.dx); + const posTop = this._getPosTop(pointerY, this.dy); + this.el.style.left = `${posLeft}px`; + this.el.style.top = `${posTop}px`; + } + } + endDrag() { + if (this.isDragging) { + this.isDragging = false; + this.trigger('drag-end', { + loc: { top: this.el.offsetTop, left: this.el.offsetLeft }, + }); + } + } + _getEventLoc(event) { + let coordX, coordY; + if (event.touches && event.touches[0]) { + coordX = event.touches[0].clientX; + coordY = event.touches[0].clientY; + } else { + coordX = event.clientX; + coordY = event.clientY; + } + return { + x: coordX, + y: coordY, + }; + } + _getPosLeft(pointerX, dx) { + const posLeft = pointerX + dx; + if (posLeft < this.limitLeft) { + return this.limitLeft; + } else if (posLeft > this.limitRight - this.el.offsetWidth) { + return this.limitRight - this.el.offsetWidth; + } + return posLeft; + } + _getPosTop(pointerY, dy) { + const posTop = pointerY + dy; + if (posTop < this.limitTop) { + return this.limitTop; + } else if (posTop > this.limitBottom - this.el.offsetHeight) { + return this.limitBottom - this.el.offsetHeight; + } + return posTop; + } + } + Draggable.template = 'Draggable'; + + Registries.Component.add(Draggable); + + return Draggable; +}); |
