From e230fa31d4a4191152d71ee0a19c33240d434a9e Mon Sep 17 00:00:00 2001 From: ferran Date: Mon, 21 Oct 2024 11:32:26 +0200 Subject: [PATCH] MDL-83502 course: fix chrome file dropping in course page --- lib/amd/build/local/reactive/dragdrop.min.js | 2 +- lib/amd/build/local/reactive/dragdrop.min.js.map | 2 +- lib/amd/src/local/reactive/dragdrop.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/amd/build/local/reactive/dragdrop.min.js b/lib/amd/build/local/reactive/dragdrop.min.js index 7b98c099689da..5e6c1f3b07688 100644 --- a/lib/amd/build/local/reactive/dragdrop.min.js +++ b/lib/amd/build/local/reactive/dragdrop.min.js @@ -62,6 +62,6 @@ define("core/local/reactive/dragdrop",["exports","core/local/reactive/basecompon * @class core/local/reactive/dragdrop * @copyright 2021 Ferran Recio * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_basecomponent=(obj=_basecomponent)&&obj.__esModule?obj:{default:obj};let activeDropData=new Map,dragStartPoint={};class _default extends _basecomponent.default{create(parent){var _parent$name,_parent$classes,_this$parent$draggabl,_this$parent$relative;this.name="".concat(null!==(_parent$name=parent.name)&&void 0!==_parent$name?_parent$name:"unkown","_dragdrop"),this.classes=Object.assign({BODYDRAGGING:"dragging",DRAGGABLEREADY:"draggable",DROPREADY:"dropready",DRAGOVER:"dragover",DRAGGING:"dragging",DROPUP:"drop-up",DROPDOWN:"drop-down",DROPZONE:"drop-zone",DRAGICON:"dragicon"},null!==(_parent$classes=null==parent?void 0:parent.classes)&&void 0!==_parent$classes?_parent$classes:{}),this.fullregion=parent.fullregion,this.parent=parent,this.autoconfigDraggable=null===(_this$parent$draggabl=this.parent.draggable)||void 0===_this$parent$draggabl||_this$parent$draggabl,this.relativeDrag=null!==(_this$parent$relative=this.parent.relativeDrag)&&void 0!==_this$parent$relative&&_this$parent$relative,this.entercount=0,this.dropzonevisible=!1,this.ismouseover=!1}getClasses(){return this.classes}isDropzoneVisible(){return this.dropzonevisible}stateReady(){"function"==typeof this.parent.validateDropData&&(this.element.classList.add(this.classes.DROPREADY),this.addEventListener(this.element,"dragenter",this._dragEnter),this.addEventListener(this.element,"dragleave",this._dragLeave),this.addEventListener(this.element,"dragover",this._dragOver),this.addEventListener(this.element,"drop",this._drop),this.addEventListener(this.element,"mouseover",this._mouseOver),this.addEventListener(this.element,"mouseleave",this._mouseLeave)),this.autoconfigDraggable&&"function"==typeof this.parent.getDraggableData&&this.setDraggable(!0)}setDraggable(value){if("function"!=typeof this.parent.getDraggableData)throw new Error("Draggable components must have a getDraggableData method");this.element.setAttribute("draggable",value),value?(this.addEventListener(this.element,"dragstart",this._dragStart),this.addEventListener(this.element,"dragend",this._dragEnd),this.element.classList.add(this.classes.DRAGGABLEREADY)):(this.removeEventListener(this.element,"dragstart",this._dragStart),this.removeEventListener(this.element,"dragend",this._dragEnd),this.element.classList.remove(this.classes.DRAGGABLEREADY))}_mouseOver(){this.ismouseover=!0}_mouseLeave(){this.ismouseover=!1}_dragStart(event){var _this$fullregion;if(document.activeElement.matches("textarea, input"))return void event.preventDefault();const dropdata=this.parent.getDraggableData();if(!dropdata)return;dragStartPoint={pageX:event.pageX,pageY:event.pageY},event.stopPropagation(),activeDropData.set(this.reactive,dropdata),document.body.classList.add(this.classes.BODYDRAGGING),this.element.classList.add(this.classes.DRAGGING),null===(_this$fullregion=this.fullregion)||void 0===_this$fullregion||_this$fullregion.classList.add(this.classes.DRAGGING);let dragImage=this.element;if(void 0!==this.parent.setDragImage){const customImage=this.parent.setDragImage(dropdata,event);customImage&&(dragImage=customImage)}const position={x:0,y:0};this.relativeDrag&&(position.x=event.offsetX,position.y=event.offsetY),event.dataTransfer.setDragImage(dragImage,position.x,position.y),event.dataTransfer.effectAllowed="copyMove",this._callParentMethod("dragStart",dropdata,event)}_dragEnd(event){var _this$fullregion2;const dropdata=activeDropData.get(this.reactive);dropdata&&(activeDropData.delete(this.reactive),document.body.classList.remove(this.classes.BODYDRAGGING),this.element.classList.remove(this.classes.DRAGGING),null===(_this$fullregion2=this.fullregion)||void 0===_this$fullregion2||_this$fullregion2.classList.remove(this.classes.DRAGGING),this.removeAllOverlays(),this._addEventTotalMovement(event),this._callParentMethod("dragEnd",dropdata,event))}_dragEnter(event){const dropdata=this._processEvent(event);dropdata&&(this.entercount++,this.element.classList.add(this.classes.DRAGOVER),1!=this.entercount||this.dropzonevisible||(this.dropzonevisible=!0,this.element.classList.add(this.classes.DRAGOVER),this._callParentMethod("showDropZone",dropdata,event)))}_dragOver(event){const dropdata=this._processEvent(event);event.dataTransfer.dropEffect=event.altKey?"copy":"move",dropdata&&!this.dropzonevisible&&(this.dropzonevisible=!0,this.element.classList.add(this.classes.DRAGOVER),this._callParentMethod("showDropZone",dropdata,event))}_dragLeave(event){const dropdata=this._processEvent(event);dropdata&&(this.entercount--,this.entercount<=0&&this.dropzonevisible&&(this.dropzonevisible=!1,this.element.classList.remove(this.classes.DRAGOVER),this._callParentMethod("hideDropZone",dropdata,event)))}_drop(event){const dropdata=this._processEvent(event);dropdata&&(this.entercount=0,this.dropzonevisible&&(this.dropzonevisible=!1,this._callParentMethod("hideDropZone",dropdata,event)),this.element.classList.remove(this.classes.DRAGOVER),this.removeAllOverlays(),this._callParentMethod("drop",dropdata,event),dragStartPoint={})}_processEvent(event){const dropdata=this._getDropData(event);return dropdata&&this.parent.validateDropData(dropdata)?(event.preventDefault(),event.stopPropagation(),this._addEventTotalMovement(event),dropdata):null}_addEventTotalMovement(event){if(void 0===dragStartPoint.pageX||void 0===event.pageX)return;event.fixedMovementX=event.pageX-dragStartPoint.pageX,event.fixedMovementY=event.pageY-dragStartPoint.pageY,event.initialPageX=dragStartPoint.pageX,event.initialPageY=dragStartPoint.pageY;const current=this.element.getBoundingClientRect();if(event.newFixedTop=current.top+event.fixedMovementY,event.newFixedLeft=current.left+event.fixedMovementX,void 0!==this.fullregion){const current=this.fullregion.getBoundingClientRect();event.newRegionFixedxTop=current.top+event.fixedMovementY,event.newRegionFixedxLeft=current.left+event.fixedMovementX}}_callParentMethod(methodname,dropdata,event){"function"==typeof this.parent[methodname]&&this.parent[methodname](dropdata,event)}_getDropData(event){return this._isOnlyFilesDragging=this._containsOnlyFiles(event),this._isOnlyFilesDragging?void 0!==this.reactive.getFilesDraggableData&&"function"==typeof this.reactive.getFilesDraggableData?this.reactive.getFilesDraggableData(event.dataTransfer):void 0:activeDropData.get(this.reactive)}_containsOnlyFiles(event){return!!event.dataTransfer.types.includes("Files")&&event.dataTransfer.types.every((type=>"text/uri-list"!=type.toLowerCase()&&"text/html"!=type.toLowerCase()&&"text/plain"!=type.toLowerCase()))}}return _exports.default=_default,_exports.default})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_basecomponent=(obj=_basecomponent)&&obj.__esModule?obj:{default:obj};let activeDropData=new Map,dragStartPoint={};class _default extends _basecomponent.default{create(parent){var _parent$name,_parent$classes,_this$parent$draggabl,_this$parent$relative;this.name="".concat(null!==(_parent$name=parent.name)&&void 0!==_parent$name?_parent$name:"unkown","_dragdrop"),this.classes=Object.assign({BODYDRAGGING:"dragging",DRAGGABLEREADY:"draggable",DROPREADY:"dropready",DRAGOVER:"dragover",DRAGGING:"dragging",DROPUP:"drop-up",DROPDOWN:"drop-down",DROPZONE:"drop-zone",DRAGICON:"dragicon"},null!==(_parent$classes=null==parent?void 0:parent.classes)&&void 0!==_parent$classes?_parent$classes:{}),this.fullregion=parent.fullregion,this.parent=parent,this.autoconfigDraggable=null===(_this$parent$draggabl=this.parent.draggable)||void 0===_this$parent$draggabl||_this$parent$draggabl,this.relativeDrag=null!==(_this$parent$relative=this.parent.relativeDrag)&&void 0!==_this$parent$relative&&_this$parent$relative,this.entercount=0,this.dropzonevisible=!1,this.ismouseover=!1}getClasses(){return this.classes}isDropzoneVisible(){return this.dropzonevisible}stateReady(){"function"==typeof this.parent.validateDropData&&(this.element.classList.add(this.classes.DROPREADY),this.addEventListener(this.element,"dragenter",this._dragEnter),this.addEventListener(this.element,"dragleave",this._dragLeave),this.addEventListener(this.element,"dragover",this._dragOver),this.addEventListener(this.element,"drop",this._drop),this.addEventListener(this.element,"mouseover",this._mouseOver),this.addEventListener(this.element,"mouseleave",this._mouseLeave)),this.autoconfigDraggable&&"function"==typeof this.parent.getDraggableData&&this.setDraggable(!0)}setDraggable(value){if("function"!=typeof this.parent.getDraggableData)throw new Error("Draggable components must have a getDraggableData method");this.element.setAttribute("draggable",value),value?(this.addEventListener(this.element,"dragstart",this._dragStart),this.addEventListener(this.element,"dragend",this._dragEnd),this.element.classList.add(this.classes.DRAGGABLEREADY)):(this.removeEventListener(this.element,"dragstart",this._dragStart),this.removeEventListener(this.element,"dragend",this._dragEnd),this.element.classList.remove(this.classes.DRAGGABLEREADY))}_mouseOver(){this.ismouseover=!0}_mouseLeave(){this.ismouseover=!1}_dragStart(event){var _this$fullregion;if(document.activeElement.matches("textarea, input"))return void event.preventDefault();const dropdata=this.parent.getDraggableData();if(!dropdata)return;dragStartPoint={pageX:event.pageX,pageY:event.pageY},event.stopPropagation(),activeDropData.set(this.reactive,dropdata),document.body.classList.add(this.classes.BODYDRAGGING),this.element.classList.add(this.classes.DRAGGING),null===(_this$fullregion=this.fullregion)||void 0===_this$fullregion||_this$fullregion.classList.add(this.classes.DRAGGING);let dragImage=this.element;if(void 0!==this.parent.setDragImage){const customImage=this.parent.setDragImage(dropdata,event);customImage&&(dragImage=customImage)}const position={x:0,y:0};this.relativeDrag&&(position.x=event.offsetX,position.y=event.offsetY),event.dataTransfer.setDragImage(dragImage,position.x,position.y),event.dataTransfer.effectAllowed="copyMove",this._callParentMethod("dragStart",dropdata,event)}_dragEnd(event){var _this$fullregion2;const dropdata=activeDropData.get(this.reactive);dropdata&&(activeDropData.delete(this.reactive),document.body.classList.remove(this.classes.BODYDRAGGING),this.element.classList.remove(this.classes.DRAGGING),null===(_this$fullregion2=this.fullregion)||void 0===_this$fullregion2||_this$fullregion2.classList.remove(this.classes.DRAGGING),this.removeAllOverlays(),this._addEventTotalMovement(event),this._callParentMethod("dragEnd",dropdata,event))}_dragEnter(event){const dropdata=this._processEvent(event);dropdata&&(this.entercount++,this.element.classList.add(this.classes.DRAGOVER),1!=this.entercount||this.dropzonevisible||(this.dropzonevisible=!0,this.element.classList.add(this.classes.DRAGOVER),this._callParentMethod("showDropZone",dropdata,event)))}_dragOver(event){const dropdata=this._processEvent(event);dropdata&&!this.dropzonevisible&&(event.dataTransfer.dropEffect=event.altKey?"copy":"move",this.dropzonevisible=!0,this.element.classList.add(this.classes.DRAGOVER),this._callParentMethod("showDropZone",dropdata,event))}_dragLeave(event){const dropdata=this._processEvent(event);dropdata&&(this.entercount--,this.entercount<=0&&this.dropzonevisible&&(this.dropzonevisible=!1,this.element.classList.remove(this.classes.DRAGOVER),this._callParentMethod("hideDropZone",dropdata,event)))}_drop(event){const dropdata=this._processEvent(event);dropdata&&(this.entercount=0,this.dropzonevisible&&(this.dropzonevisible=!1,this._callParentMethod("hideDropZone",dropdata,event)),this.element.classList.remove(this.classes.DRAGOVER),this.removeAllOverlays(),this._callParentMethod("drop",dropdata,event),dragStartPoint={})}_processEvent(event){const dropdata=this._getDropData(event);return dropdata&&this.parent.validateDropData(dropdata)?(event.preventDefault(),event.stopPropagation(),this._addEventTotalMovement(event),dropdata):null}_addEventTotalMovement(event){if(void 0===dragStartPoint.pageX||void 0===event.pageX)return;event.fixedMovementX=event.pageX-dragStartPoint.pageX,event.fixedMovementY=event.pageY-dragStartPoint.pageY,event.initialPageX=dragStartPoint.pageX,event.initialPageY=dragStartPoint.pageY;const current=this.element.getBoundingClientRect();if(event.newFixedTop=current.top+event.fixedMovementY,event.newFixedLeft=current.left+event.fixedMovementX,void 0!==this.fullregion){const current=this.fullregion.getBoundingClientRect();event.newRegionFixedxTop=current.top+event.fixedMovementY,event.newRegionFixedxLeft=current.left+event.fixedMovementX}}_callParentMethod(methodname,dropdata,event){"function"==typeof this.parent[methodname]&&this.parent[methodname](dropdata,event)}_getDropData(event){return this._isOnlyFilesDragging=this._containsOnlyFiles(event),this._isOnlyFilesDragging?void 0!==this.reactive.getFilesDraggableData&&"function"==typeof this.reactive.getFilesDraggableData?this.reactive.getFilesDraggableData(event.dataTransfer):void 0:activeDropData.get(this.reactive)}_containsOnlyFiles(event){return!!event.dataTransfer.types.includes("Files")&&event.dataTransfer.types.every((type=>"text/uri-list"!=type.toLowerCase()&&"text/html"!=type.toLowerCase()&&"text/plain"!=type.toLowerCase()))}}return _exports.default=_default,_exports.default})); //# sourceMappingURL=dragdrop.min.js.map \ No newline at end of file diff --git a/lib/amd/build/local/reactive/dragdrop.min.js.map b/lib/amd/build/local/reactive/dragdrop.min.js.map index 7d80dbc80dcfd..fd2921f8f2ddc 100644 --- a/lib/amd/build/local/reactive/dragdrop.min.js.map +++ b/lib/amd/build/local/reactive/dragdrop.min.js.map @@ -1 +1 @@ -{"version":3,"file":"dragdrop.min.js","sources":["../../../src/local/reactive/dragdrop.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Drag and drop helper component.\n *\n * This component is used to delegate drag and drop handling.\n *\n * To delegate the logic to this particular element the component should create a new instance\n * passing \"this\" as param. The component will use all the necessary callbacks and add all the\n * necessary listeners to the component element.\n *\n * Component attributes used by dragdrop module:\n * - element: the draggable or dropzone element.\n * - (optional) classes: object with alternative CSS classes\n * - (optional) fullregion: page element affeted by the elementy dragging. Use this attribute if\n * the draggable element affects a bigger region (for example a draggable\n * title).\n * - (optional) autoconfigDraggable: by default, the component will be draggable if it has a\n * getDraggableData method. If this value is false draggable\n * property must be defined using setDraggable method.\n * - (optional) relativeDrag: by default the drag image is located at point (0,0) relative to the\n * mouse position to prevent the mouse from covering it. If this attribute\n * is true the drag image will be located at the click offset.\n *\n * Methods the parent component should have for making it draggable:\n *\n * - getDraggableData(): Object|data\n * Return the data that will be passed to any valid dropzone while it is dragged.\n * If the component has this method, the dragdrop module will enable the dragging,\n * this is the only required method for dragging.\n * If at the dragging moment this method returns a false|null|undefined, the dragging\n * actions won't be captured.\n *\n * - (optional) dragStart(Object dropdata, Event event): void\n * - (optional) dragEnd(Object dropdata, Event event): void\n * Callbacks dragdrop will call when the element is dragged and getDraggableData\n * return some data.\n *\n * Methods the parent component should have for enabling it as a dropzone:\n *\n * - validateDropData(Object dropdata): boolean\n * If that method exists, the dragdrop module will automathically configure the element as dropzone.\n * This method will return true if the dropdata is accepted. In case it returns false, no drag and\n * drop event will be listened for this specific dragged dropdata.\n *\n * - (Optional) showDropZone(Object dropdata, Event event): void\n * - (Optional) hideDropZone(Object dropdata, Event event): void\n * Methods called when a valid dragged data pass over the element.\n *\n * - (Optional) drop(Object dropdata, Event event): void\n * Called when a valid dragged element is dropped over the element.\n *\n * Note that none of this methods will be called if validateDropData\n * returns a false value.\n *\n * This module will also add or remove several CSS classes from both dragged elements and dropzones.\n * See the \"this.classes\" in the create method for more details. In case the parent component wants\n * to use the same classes, it can use the getClasses method. On the other hand, if the parent\n * component has an alternative \"classes\" attribute, this will override the default drag and drop\n * classes.\n *\n * @module core/local/reactive/dragdrop\n * @class core/local/reactive/dragdrop\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport BaseComponent from 'core/local/reactive/basecomponent';\n\n// Map with the dragged element generate by an specific reactive applications.\n// Potentially, any component can generate a draggable element to interact with other\n// page elements. However, the dragged data is specific and could only interact with\n// components of the same reactive instance.\nlet activeDropData = new Map();\n\n// Drag & Drop API provides the final drop point and incremental movements but we can\n// provide also starting points and displacements. Absolute displacements simplifies\n// moving components with aboslute position around the page.\nlet dragStartPoint = {};\n\nexport default class extends BaseComponent {\n\n /**\n * Constructor hook.\n *\n * @param {BaseComponent} parent the parent component.\n */\n create(parent) {\n // Optional component name for debugging.\n this.name = `${parent.name ?? 'unkown'}_dragdrop`;\n\n // Default drag and drop classes.\n this.classes = Object.assign(\n {\n // This class indicate a dragging action is active at a page level.\n BODYDRAGGING: 'dragging',\n\n // Added when draggable and drop are ready.\n DRAGGABLEREADY: 'draggable',\n DROPREADY: 'dropready',\n\n // When a valid drag element is over the element.\n DRAGOVER: 'dragover',\n // When a the component is dragged.\n DRAGGING: 'dragging',\n\n // Dropzones classes names.\n DROPUP: 'drop-up',\n DROPDOWN: 'drop-down',\n DROPZONE: 'drop-zone',\n\n // Drag icon class.\n DRAGICON: 'dragicon',\n },\n parent?.classes ?? {}\n );\n\n // Add the affected region if any.\n this.fullregion = parent.fullregion;\n\n // Keep parent to execute drap and drop handlers.\n this.parent = parent;\n\n // Check if parent handle draggable manually.\n this.autoconfigDraggable = this.parent.draggable ?? true;\n\n // Drag image relative position.\n this.relativeDrag = this.parent.relativeDrag ?? false;\n\n // Sub HTML elements will trigger extra dragEnter and dragOver all the time.\n // To prevent that from affecting dropzones, we need to count the enters and leaves.\n this.entercount = 0;\n\n // Stores if the droparea is shown or not.\n this.dropzonevisible = false;\n\n // Stores if the mouse is over the element or not.\n this.ismouseover = false;\n }\n\n /**\n * Return the component drag and drop CSS classes.\n *\n * @returns {Object} the dragdrop css classes\n */\n getClasses() {\n return this.classes;\n }\n\n /**\n * Return the current drop-zone visible of the element.\n *\n * @returns {boolean} if the dropzone should be visible or not\n */\n isDropzoneVisible() {\n return this.dropzonevisible;\n }\n\n /**\n * Initial state ready method.\n *\n * This method will add all the necessary event listeners to the component depending on the\n * parent methods.\n * - Add drop events to the element if the parent component has validateDropData method.\n * - Configure the elements draggable if the parent component has getDraggableData method.\n */\n stateReady() {\n // Add drop events to the element if the parent component has dropable types.\n if (typeof this.parent.validateDropData === 'function') {\n this.element.classList.add(this.classes.DROPREADY);\n this.addEventListener(this.element, 'dragenter', this._dragEnter);\n this.addEventListener(this.element, 'dragleave', this._dragLeave);\n this.addEventListener(this.element, 'dragover', this._dragOver);\n this.addEventListener(this.element, 'drop', this._drop);\n this.addEventListener(this.element, 'mouseover', this._mouseOver);\n this.addEventListener(this.element, 'mouseleave', this._mouseLeave);\n }\n\n // Configure the elements draggable if the parent component has dragable data.\n if (this.autoconfigDraggable && typeof this.parent.getDraggableData === 'function') {\n this.setDraggable(true);\n }\n }\n\n /**\n * Enable or disable the draggable property.\n *\n * @param {bool} value the new draggable value\n */\n setDraggable(value) {\n if (typeof this.parent.getDraggableData !== 'function') {\n throw new Error(`Draggable components must have a getDraggableData method`);\n }\n this.element.setAttribute('draggable', value);\n if (value) {\n this.addEventListener(this.element, 'dragstart', this._dragStart);\n this.addEventListener(this.element, 'dragend', this._dragEnd);\n this.element.classList.add(this.classes.DRAGGABLEREADY);\n } else {\n this.removeEventListener(this.element, 'dragstart', this._dragStart);\n this.removeEventListener(this.element, 'dragend', this._dragEnd);\n this.element.classList.remove(this.classes.DRAGGABLEREADY);\n }\n }\n\n /**\n * Mouse over handle.\n */\n _mouseOver() {\n this.ismouseover = true;\n }\n\n /**\n * Mouse leave handler.\n */\n _mouseLeave() {\n this.ismouseover = false;\n }\n\n /**\n * Drag start event handler.\n *\n * This method will generate the current dropable data. This data is the one used to determine\n * if a droparea accepts the dropping or not.\n *\n * @param {Event} event the event.\n */\n _dragStart(event) {\n // Cancel dragging if any editable form element is focussed.\n if (document.activeElement.matches(`textarea, input`)) {\n event.preventDefault();\n return;\n }\n\n const dropdata = this.parent.getDraggableData();\n if (!dropdata) {\n return;\n }\n\n // Save the starting point.\n dragStartPoint = {\n pageX: event.pageX,\n pageY: event.pageY,\n };\n\n // If the drag event is accepted we prevent any other draggable element from interfiering.\n event.stopPropagation();\n\n // Save the drop data of the current reactive intance.\n activeDropData.set(this.reactive, dropdata);\n\n // Add some CSS classes to indicate the state.\n document.body.classList.add(this.classes.BODYDRAGGING);\n this.element.classList.add(this.classes.DRAGGING);\n this.fullregion?.classList.add(this.classes.DRAGGING);\n\n // Force the drag image. This makes the UX more consistent in case the\n // user dragged an internal element like a link or some other element.\n let dragImage = this.element;\n if (this.parent.setDragImage !== undefined) {\n const customImage = this.parent.setDragImage(dropdata, event);\n if (customImage) {\n dragImage = customImage;\n }\n }\n // Define the image position relative to the mouse.\n const position = {x: 0, y: 0};\n if (this.relativeDrag) {\n position.x = event.offsetX;\n position.y = event.offsetY;\n }\n event.dataTransfer.setDragImage(dragImage, position.x, position.y);\n event.dataTransfer.effectAllowed = 'copyMove';\n this._callParentMethod('dragStart', dropdata, event);\n }\n\n /**\n * Drag end event handler.\n *\n * @param {Event} event the event.\n */\n _dragEnd(event) {\n const dropdata = activeDropData.get(this.reactive);\n if (!dropdata) {\n return;\n }\n\n // Remove the current dropdata.\n activeDropData.delete(this.reactive);\n\n // Remove the dragging classes.\n document.body.classList.remove(this.classes.BODYDRAGGING);\n this.element.classList.remove(this.classes.DRAGGING);\n this.fullregion?.classList.remove(this.classes.DRAGGING);\n this.removeAllOverlays();\n\n // We add the total movement to the event in case the component\n // wants to move its absolute position.\n this._addEventTotalMovement(event);\n\n this._callParentMethod('dragEnd', dropdata, event);\n }\n\n /**\n * Drag enter event handler.\n *\n * The JS drag&drop API triggers several dragenter events on the same element because it bubbles the\n * child events as well. To prevent this form affecting the dropzones display, this methods use\n * \"entercount\" to determine if it's one extra child event or a valid one.\n *\n * @param {Event} event the event.\n */\n _dragEnter(event) {\n const dropdata = this._processEvent(event);\n if (dropdata) {\n this.entercount++;\n this.element.classList.add(this.classes.DRAGOVER);\n if (this.entercount == 1 && !this.dropzonevisible) {\n this.dropzonevisible = true;\n this.element.classList.add(this.classes.DRAGOVER);\n this._callParentMethod('showDropZone', dropdata, event);\n }\n }\n }\n\n /**\n * Drag over event handler.\n *\n * We only use dragover event when a draggable action starts inside a valid dropzone. In those cases\n * the API won't trigger any dragEnter because the dragged alement was already there. We use the\n * dropzonevisible to determine if the component needs to display the dropzones or not.\n *\n * @param {Event} event the event.\n */\n _dragOver(event) {\n const dropdata = this._processEvent(event);\n event.dataTransfer.dropEffect = (event.altKey) ? 'copy' : 'move';\n if (dropdata && !this.dropzonevisible) {\n this.dropzonevisible = true;\n this.element.classList.add(this.classes.DRAGOVER);\n this._callParentMethod('showDropZone', dropdata, event);\n }\n }\n\n /**\n * Drag over leave handler.\n *\n * The JS drag&drop API triggers several dragleave events on the same element because it bubbles the\n * child events as well. To prevent this form affecting the dropzones display, this methods use\n * \"entercount\" to determine if it's one extra child event or a valid one.\n *\n * @param {Event} event the event.\n */\n _dragLeave(event) {\n const dropdata = this._processEvent(event);\n if (dropdata) {\n this.entercount--;\n if (this.entercount <= 0 && this.dropzonevisible) {\n this.dropzonevisible = false;\n this.element.classList.remove(this.classes.DRAGOVER);\n this._callParentMethod('hideDropZone', dropdata, event);\n }\n }\n }\n\n /**\n * Drop event handler.\n *\n * This method will call both hideDropZones and drop methods on the parent component.\n *\n * @param {Event} event the event.\n */\n _drop(event) {\n const dropdata = this._processEvent(event);\n if (dropdata) {\n this.entercount = 0;\n if (this.dropzonevisible) {\n this.dropzonevisible = false;\n this._callParentMethod('hideDropZone', dropdata, event);\n }\n this.element.classList.remove(this.classes.DRAGOVER);\n this.removeAllOverlays();\n this._callParentMethod('drop', dropdata, event);\n // An accepted drop resets the initial position.\n // Save the starting point.\n dragStartPoint = {};\n }\n }\n\n /**\n * Process a drag and drop event and delegate logic to the parent component.\n *\n * @param {Event} event the drag and drop event\n * @return {Object|false} the dropdata or null if the event should not be processed\n */\n _processEvent(event) {\n const dropdata = this._getDropData(event);\n if (!dropdata) {\n return null;\n }\n if (this.parent.validateDropData(dropdata)) {\n // All accepted drag&drop event must prevent bubbling and defaults, otherwise\n // parent dragdrop instances could capture it by mistake.\n event.preventDefault();\n event.stopPropagation();\n this._addEventTotalMovement(event);\n return dropdata;\n }\n return null;\n }\n\n /**\n * Add the total amout of movement to a mouse event.\n *\n * @param {MouseEvent} event\n */\n _addEventTotalMovement(event) {\n if (dragStartPoint.pageX === undefined || event.pageX === undefined) {\n return;\n }\n event.fixedMovementX = event.pageX - dragStartPoint.pageX;\n event.fixedMovementY = event.pageY - dragStartPoint.pageY;\n event.initialPageX = dragStartPoint.pageX;\n event.initialPageY = dragStartPoint.pageY;\n // The element possible new top.\n const current = this.element.getBoundingClientRect();\n // Add the new position fixed position.\n event.newFixedTop = current.top + event.fixedMovementY;\n event.newFixedLeft = current.left + event.fixedMovementX;\n // The affected region possible new top.\n if (this.fullregion !== undefined) {\n const current = this.fullregion.getBoundingClientRect();\n event.newRegionFixedxTop = current.top + event.fixedMovementY;\n event.newRegionFixedxLeft = current.left + event.fixedMovementX;\n }\n }\n\n /**\n * Convenient method for calling parent component functions if present.\n *\n * @param {string} methodname the name of the method\n * @param {Object} dropdata the current drop data object\n * @param {Event} event the original event\n */\n _callParentMethod(methodname, dropdata, event) {\n if (typeof this.parent[methodname] === 'function') {\n this.parent[methodname](dropdata, event);\n }\n }\n\n /**\n * Get the current dropdata for a specific event.\n *\n * The browser can generate drag&drop events related to several user interactions:\n * - Drag a page elements: this case is registered in the activeDropData map\n * - Drag some HTML selections: ignored for now\n * - Drag a file over the browser: file drag may appear in the future but for now they are ignored.\n *\n * @param {Event} event the original event.\n * @returns {Object|undefined} with the dragged data (or undefined if none)\n */\n _getDropData(event) {\n this._isOnlyFilesDragging = this._containsOnlyFiles(event);\n if (this._isOnlyFilesDragging) {\n // Check if the reactive instance can provide a files draggable data.\n if (this.reactive.getFilesDraggableData !== undefined && typeof this.reactive.getFilesDraggableData === 'function') {\n return this.reactive.getFilesDraggableData(event.dataTransfer);\n }\n return undefined;\n }\n return activeDropData.get(this.reactive);\n }\n\n /**\n * Check if the dragged event contains only files.\n *\n * Files dragging does not generate drop data because they came from outside the page and the component\n * must check it before validating the event.\n *\n * Some browsers like Firefox add extra types to file dragging. To discard the false positives\n * a double check is necessary.\n *\n * @param {Event} event the original event.\n * @returns {boolean} if the drag dataTransfers contains files.\n */\n _containsOnlyFiles(event) {\n if (!event.dataTransfer.types.includes('Files')) {\n return false;\n }\n return event.dataTransfer.types.every((type) => {\n return (type.toLowerCase() != 'text/uri-list'\n && type.toLowerCase() != 'text/html'\n && type.toLowerCase() != 'text/plain'\n );\n });\n }\n}\n"],"names":["activeDropData","Map","dragStartPoint","BaseComponent","create","parent","name","classes","Object","assign","BODYDRAGGING","DRAGGABLEREADY","DROPREADY","DRAGOVER","DRAGGING","DROPUP","DROPDOWN","DROPZONE","DRAGICON","fullregion","autoconfigDraggable","this","draggable","relativeDrag","entercount","dropzonevisible","ismouseover","getClasses","isDropzoneVisible","stateReady","validateDropData","element","classList","add","addEventListener","_dragEnter","_dragLeave","_dragOver","_drop","_mouseOver","_mouseLeave","getDraggableData","setDraggable","value","Error","setAttribute","_dragStart","_dragEnd","removeEventListener","remove","event","document","activeElement","matches","preventDefault","dropdata","pageX","pageY","stopPropagation","set","reactive","body","dragImage","undefined","setDragImage","customImage","position","x","y","offsetX","offsetY","dataTransfer","effectAllowed","_callParentMethod","get","delete","removeAllOverlays","_addEventTotalMovement","_processEvent","dropEffect","altKey","_getDropData","fixedMovementX","fixedMovementY","initialPageX","initialPageY","current","getBoundingClientRect","newFixedTop","top","newFixedLeft","left","newRegionFixedxTop","newRegionFixedxLeft","methodname","_isOnlyFilesDragging","_containsOnlyFiles","getFilesDraggableData","types","includes","every","type","toLowerCase"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"dragdrop.min.js","sources":["../../../src/local/reactive/dragdrop.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Drag and drop helper component.\n *\n * This component is used to delegate drag and drop handling.\n *\n * To delegate the logic to this particular element the component should create a new instance\n * passing \"this\" as param. The component will use all the necessary callbacks and add all the\n * necessary listeners to the component element.\n *\n * Component attributes used by dragdrop module:\n * - element: the draggable or dropzone element.\n * - (optional) classes: object with alternative CSS classes\n * - (optional) fullregion: page element affeted by the elementy dragging. Use this attribute if\n * the draggable element affects a bigger region (for example a draggable\n * title).\n * - (optional) autoconfigDraggable: by default, the component will be draggable if it has a\n * getDraggableData method. If this value is false draggable\n * property must be defined using setDraggable method.\n * - (optional) relativeDrag: by default the drag image is located at point (0,0) relative to the\n * mouse position to prevent the mouse from covering it. If this attribute\n * is true the drag image will be located at the click offset.\n *\n * Methods the parent component should have for making it draggable:\n *\n * - getDraggableData(): Object|data\n * Return the data that will be passed to any valid dropzone while it is dragged.\n * If the component has this method, the dragdrop module will enable the dragging,\n * this is the only required method for dragging.\n * If at the dragging moment this method returns a false|null|undefined, the dragging\n * actions won't be captured.\n *\n * - (optional) dragStart(Object dropdata, Event event): void\n * - (optional) dragEnd(Object dropdata, Event event): void\n * Callbacks dragdrop will call when the element is dragged and getDraggableData\n * return some data.\n *\n * Methods the parent component should have for enabling it as a dropzone:\n *\n * - validateDropData(Object dropdata): boolean\n * If that method exists, the dragdrop module will automathically configure the element as dropzone.\n * This method will return true if the dropdata is accepted. In case it returns false, no drag and\n * drop event will be listened for this specific dragged dropdata.\n *\n * - (Optional) showDropZone(Object dropdata, Event event): void\n * - (Optional) hideDropZone(Object dropdata, Event event): void\n * Methods called when a valid dragged data pass over the element.\n *\n * - (Optional) drop(Object dropdata, Event event): void\n * Called when a valid dragged element is dropped over the element.\n *\n * Note that none of this methods will be called if validateDropData\n * returns a false value.\n *\n * This module will also add or remove several CSS classes from both dragged elements and dropzones.\n * See the \"this.classes\" in the create method for more details. In case the parent component wants\n * to use the same classes, it can use the getClasses method. On the other hand, if the parent\n * component has an alternative \"classes\" attribute, this will override the default drag and drop\n * classes.\n *\n * @module core/local/reactive/dragdrop\n * @class core/local/reactive/dragdrop\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport BaseComponent from 'core/local/reactive/basecomponent';\n\n// Map with the dragged element generate by an specific reactive applications.\n// Potentially, any component can generate a draggable element to interact with other\n// page elements. However, the dragged data is specific and could only interact with\n// components of the same reactive instance.\nlet activeDropData = new Map();\n\n// Drag & Drop API provides the final drop point and incremental movements but we can\n// provide also starting points and displacements. Absolute displacements simplifies\n// moving components with aboslute position around the page.\nlet dragStartPoint = {};\n\nexport default class extends BaseComponent {\n\n /**\n * Constructor hook.\n *\n * @param {BaseComponent} parent the parent component.\n */\n create(parent) {\n // Optional component name for debugging.\n this.name = `${parent.name ?? 'unkown'}_dragdrop`;\n\n // Default drag and drop classes.\n this.classes = Object.assign(\n {\n // This class indicate a dragging action is active at a page level.\n BODYDRAGGING: 'dragging',\n\n // Added when draggable and drop are ready.\n DRAGGABLEREADY: 'draggable',\n DROPREADY: 'dropready',\n\n // When a valid drag element is over the element.\n DRAGOVER: 'dragover',\n // When a the component is dragged.\n DRAGGING: 'dragging',\n\n // Dropzones classes names.\n DROPUP: 'drop-up',\n DROPDOWN: 'drop-down',\n DROPZONE: 'drop-zone',\n\n // Drag icon class.\n DRAGICON: 'dragicon',\n },\n parent?.classes ?? {}\n );\n\n // Add the affected region if any.\n this.fullregion = parent.fullregion;\n\n // Keep parent to execute drap and drop handlers.\n this.parent = parent;\n\n // Check if parent handle draggable manually.\n this.autoconfigDraggable = this.parent.draggable ?? true;\n\n // Drag image relative position.\n this.relativeDrag = this.parent.relativeDrag ?? false;\n\n // Sub HTML elements will trigger extra dragEnter and dragOver all the time.\n // To prevent that from affecting dropzones, we need to count the enters and leaves.\n this.entercount = 0;\n\n // Stores if the droparea is shown or not.\n this.dropzonevisible = false;\n\n // Stores if the mouse is over the element or not.\n this.ismouseover = false;\n }\n\n /**\n * Return the component drag and drop CSS classes.\n *\n * @returns {Object} the dragdrop css classes\n */\n getClasses() {\n return this.classes;\n }\n\n /**\n * Return the current drop-zone visible of the element.\n *\n * @returns {boolean} if the dropzone should be visible or not\n */\n isDropzoneVisible() {\n return this.dropzonevisible;\n }\n\n /**\n * Initial state ready method.\n *\n * This method will add all the necessary event listeners to the component depending on the\n * parent methods.\n * - Add drop events to the element if the parent component has validateDropData method.\n * - Configure the elements draggable if the parent component has getDraggableData method.\n */\n stateReady() {\n // Add drop events to the element if the parent component has dropable types.\n if (typeof this.parent.validateDropData === 'function') {\n this.element.classList.add(this.classes.DROPREADY);\n this.addEventListener(this.element, 'dragenter', this._dragEnter);\n this.addEventListener(this.element, 'dragleave', this._dragLeave);\n this.addEventListener(this.element, 'dragover', this._dragOver);\n this.addEventListener(this.element, 'drop', this._drop);\n this.addEventListener(this.element, 'mouseover', this._mouseOver);\n this.addEventListener(this.element, 'mouseleave', this._mouseLeave);\n }\n\n // Configure the elements draggable if the parent component has dragable data.\n if (this.autoconfigDraggable && typeof this.parent.getDraggableData === 'function') {\n this.setDraggable(true);\n }\n }\n\n /**\n * Enable or disable the draggable property.\n *\n * @param {bool} value the new draggable value\n */\n setDraggable(value) {\n if (typeof this.parent.getDraggableData !== 'function') {\n throw new Error(`Draggable components must have a getDraggableData method`);\n }\n this.element.setAttribute('draggable', value);\n if (value) {\n this.addEventListener(this.element, 'dragstart', this._dragStart);\n this.addEventListener(this.element, 'dragend', this._dragEnd);\n this.element.classList.add(this.classes.DRAGGABLEREADY);\n } else {\n this.removeEventListener(this.element, 'dragstart', this._dragStart);\n this.removeEventListener(this.element, 'dragend', this._dragEnd);\n this.element.classList.remove(this.classes.DRAGGABLEREADY);\n }\n }\n\n /**\n * Mouse over handle.\n */\n _mouseOver() {\n this.ismouseover = true;\n }\n\n /**\n * Mouse leave handler.\n */\n _mouseLeave() {\n this.ismouseover = false;\n }\n\n /**\n * Drag start event handler.\n *\n * This method will generate the current dropable data. This data is the one used to determine\n * if a droparea accepts the dropping or not.\n *\n * @param {Event} event the event.\n */\n _dragStart(event) {\n // Cancel dragging if any editable form element is focussed.\n if (document.activeElement.matches(`textarea, input`)) {\n event.preventDefault();\n return;\n }\n\n const dropdata = this.parent.getDraggableData();\n if (!dropdata) {\n return;\n }\n\n // Save the starting point.\n dragStartPoint = {\n pageX: event.pageX,\n pageY: event.pageY,\n };\n\n // If the drag event is accepted we prevent any other draggable element from interfiering.\n event.stopPropagation();\n\n // Save the drop data of the current reactive intance.\n activeDropData.set(this.reactive, dropdata);\n\n // Add some CSS classes to indicate the state.\n document.body.classList.add(this.classes.BODYDRAGGING);\n this.element.classList.add(this.classes.DRAGGING);\n this.fullregion?.classList.add(this.classes.DRAGGING);\n\n // Force the drag image. This makes the UX more consistent in case the\n // user dragged an internal element like a link or some other element.\n let dragImage = this.element;\n if (this.parent.setDragImage !== undefined) {\n const customImage = this.parent.setDragImage(dropdata, event);\n if (customImage) {\n dragImage = customImage;\n }\n }\n // Define the image position relative to the mouse.\n const position = {x: 0, y: 0};\n if (this.relativeDrag) {\n position.x = event.offsetX;\n position.y = event.offsetY;\n }\n event.dataTransfer.setDragImage(dragImage, position.x, position.y);\n event.dataTransfer.effectAllowed = 'copyMove';\n this._callParentMethod('dragStart', dropdata, event);\n }\n\n /**\n * Drag end event handler.\n *\n * @param {Event} event the event.\n */\n _dragEnd(event) {\n const dropdata = activeDropData.get(this.reactive);\n if (!dropdata) {\n return;\n }\n\n // Remove the current dropdata.\n activeDropData.delete(this.reactive);\n\n // Remove the dragging classes.\n document.body.classList.remove(this.classes.BODYDRAGGING);\n this.element.classList.remove(this.classes.DRAGGING);\n this.fullregion?.classList.remove(this.classes.DRAGGING);\n this.removeAllOverlays();\n\n // We add the total movement to the event in case the component\n // wants to move its absolute position.\n this._addEventTotalMovement(event);\n\n this._callParentMethod('dragEnd', dropdata, event);\n }\n\n /**\n * Drag enter event handler.\n *\n * The JS drag&drop API triggers several dragenter events on the same element because it bubbles the\n * child events as well. To prevent this form affecting the dropzones display, this methods use\n * \"entercount\" to determine if it's one extra child event or a valid one.\n *\n * @param {Event} event the event.\n */\n _dragEnter(event) {\n const dropdata = this._processEvent(event);\n if (dropdata) {\n this.entercount++;\n this.element.classList.add(this.classes.DRAGOVER);\n if (this.entercount == 1 && !this.dropzonevisible) {\n this.dropzonevisible = true;\n this.element.classList.add(this.classes.DRAGOVER);\n this._callParentMethod('showDropZone', dropdata, event);\n }\n }\n }\n\n /**\n * Drag over event handler.\n *\n * We only use dragover event when a draggable action starts inside a valid dropzone. In those cases\n * the API won't trigger any dragEnter because the dragged alement was already there. We use the\n * dropzonevisible to determine if the component needs to display the dropzones or not.\n *\n * @param {Event} event the event.\n */\n _dragOver(event) {\n const dropdata = this._processEvent(event);\n if (dropdata && !this.dropzonevisible) {\n event.dataTransfer.dropEffect = (event.altKey) ? 'copy' : 'move';\n this.dropzonevisible = true;\n this.element.classList.add(this.classes.DRAGOVER);\n this._callParentMethod('showDropZone', dropdata, event);\n }\n }\n\n /**\n * Drag over leave handler.\n *\n * The JS drag&drop API triggers several dragleave events on the same element because it bubbles the\n * child events as well. To prevent this form affecting the dropzones display, this methods use\n * \"entercount\" to determine if it's one extra child event or a valid one.\n *\n * @param {Event} event the event.\n */\n _dragLeave(event) {\n const dropdata = this._processEvent(event);\n if (dropdata) {\n this.entercount--;\n if (this.entercount <= 0 && this.dropzonevisible) {\n this.dropzonevisible = false;\n this.element.classList.remove(this.classes.DRAGOVER);\n this._callParentMethod('hideDropZone', dropdata, event);\n }\n }\n }\n\n /**\n * Drop event handler.\n *\n * This method will call both hideDropZones and drop methods on the parent component.\n *\n * @param {Event} event the event.\n */\n _drop(event) {\n const dropdata = this._processEvent(event);\n if (dropdata) {\n this.entercount = 0;\n if (this.dropzonevisible) {\n this.dropzonevisible = false;\n this._callParentMethod('hideDropZone', dropdata, event);\n }\n this.element.classList.remove(this.classes.DRAGOVER);\n this.removeAllOverlays();\n this._callParentMethod('drop', dropdata, event);\n // An accepted drop resets the initial position.\n // Save the starting point.\n dragStartPoint = {};\n }\n }\n\n /**\n * Process a drag and drop event and delegate logic to the parent component.\n *\n * @param {Event} event the drag and drop event\n * @return {Object|false} the dropdata or null if the event should not be processed\n */\n _processEvent(event) {\n const dropdata = this._getDropData(event);\n if (!dropdata) {\n return null;\n }\n if (this.parent.validateDropData(dropdata)) {\n // All accepted drag&drop event must prevent bubbling and defaults, otherwise\n // parent dragdrop instances could capture it by mistake.\n event.preventDefault();\n event.stopPropagation();\n this._addEventTotalMovement(event);\n return dropdata;\n }\n return null;\n }\n\n /**\n * Add the total amout of movement to a mouse event.\n *\n * @param {MouseEvent} event\n */\n _addEventTotalMovement(event) {\n if (dragStartPoint.pageX === undefined || event.pageX === undefined) {\n return;\n }\n event.fixedMovementX = event.pageX - dragStartPoint.pageX;\n event.fixedMovementY = event.pageY - dragStartPoint.pageY;\n event.initialPageX = dragStartPoint.pageX;\n event.initialPageY = dragStartPoint.pageY;\n // The element possible new top.\n const current = this.element.getBoundingClientRect();\n // Add the new position fixed position.\n event.newFixedTop = current.top + event.fixedMovementY;\n event.newFixedLeft = current.left + event.fixedMovementX;\n // The affected region possible new top.\n if (this.fullregion !== undefined) {\n const current = this.fullregion.getBoundingClientRect();\n event.newRegionFixedxTop = current.top + event.fixedMovementY;\n event.newRegionFixedxLeft = current.left + event.fixedMovementX;\n }\n }\n\n /**\n * Convenient method for calling parent component functions if present.\n *\n * @param {string} methodname the name of the method\n * @param {Object} dropdata the current drop data object\n * @param {Event} event the original event\n */\n _callParentMethod(methodname, dropdata, event) {\n if (typeof this.parent[methodname] === 'function') {\n this.parent[methodname](dropdata, event);\n }\n }\n\n /**\n * Get the current dropdata for a specific event.\n *\n * The browser can generate drag&drop events related to several user interactions:\n * - Drag a page elements: this case is registered in the activeDropData map\n * - Drag some HTML selections: ignored for now\n * - Drag a file over the browser: file drag may appear in the future but for now they are ignored.\n *\n * @param {Event} event the original event.\n * @returns {Object|undefined} with the dragged data (or undefined if none)\n */\n _getDropData(event) {\n this._isOnlyFilesDragging = this._containsOnlyFiles(event);\n if (this._isOnlyFilesDragging) {\n // Check if the reactive instance can provide a files draggable data.\n if (this.reactive.getFilesDraggableData !== undefined && typeof this.reactive.getFilesDraggableData === 'function') {\n return this.reactive.getFilesDraggableData(event.dataTransfer);\n }\n return undefined;\n }\n return activeDropData.get(this.reactive);\n }\n\n /**\n * Check if the dragged event contains only files.\n *\n * Files dragging does not generate drop data because they came from outside the page and the component\n * must check it before validating the event.\n *\n * Some browsers like Firefox add extra types to file dragging. To discard the false positives\n * a double check is necessary.\n *\n * @param {Event} event the original event.\n * @returns {boolean} if the drag dataTransfers contains files.\n */\n _containsOnlyFiles(event) {\n if (!event.dataTransfer.types.includes('Files')) {\n return false;\n }\n return event.dataTransfer.types.every((type) => {\n return (type.toLowerCase() != 'text/uri-list'\n && type.toLowerCase() != 'text/html'\n && type.toLowerCase() != 'text/plain'\n );\n });\n }\n}\n"],"names":["activeDropData","Map","dragStartPoint","BaseComponent","create","parent","name","classes","Object","assign","BODYDRAGGING","DRAGGABLEREADY","DROPREADY","DRAGOVER","DRAGGING","DROPUP","DROPDOWN","DROPZONE","DRAGICON","fullregion","autoconfigDraggable","this","draggable","relativeDrag","entercount","dropzonevisible","ismouseover","getClasses","isDropzoneVisible","stateReady","validateDropData","element","classList","add","addEventListener","_dragEnter","_dragLeave","_dragOver","_drop","_mouseOver","_mouseLeave","getDraggableData","setDraggable","value","Error","setAttribute","_dragStart","_dragEnd","removeEventListener","remove","event","document","activeElement","matches","preventDefault","dropdata","pageX","pageY","stopPropagation","set","reactive","body","dragImage","undefined","setDragImage","customImage","position","x","y","offsetX","offsetY","dataTransfer","effectAllowed","_callParentMethod","get","delete","removeAllOverlays","_addEventTotalMovement","_processEvent","dropEffect","altKey","_getDropData","fixedMovementX","fixedMovementY","initialPageX","initialPageY","current","getBoundingClientRect","newFixedTop","top","newFixedLeft","left","newRegionFixedxTop","newRegionFixedxLeft","methodname","_isOnlyFilesDragging","_containsOnlyFiles","getFilesDraggableData","types","includes","every","type","toLowerCase"],"mappings":"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"} \ No newline at end of file diff --git a/lib/amd/src/local/reactive/dragdrop.js b/lib/amd/src/local/reactive/dragdrop.js index 5abd9c1c87406..bc13a38c14796 100644 --- a/lib/amd/src/local/reactive/dragdrop.js +++ b/lib/amd/src/local/reactive/dragdrop.js @@ -347,8 +347,8 @@ export default class extends BaseComponent { */ _dragOver(event) { const dropdata = this._processEvent(event); - event.dataTransfer.dropEffect = (event.altKey) ? 'copy' : 'move'; if (dropdata && !this.dropzonevisible) { + event.dataTransfer.dropEffect = (event.altKey) ? 'copy' : 'move'; this.dropzonevisible = true; this.element.classList.add(this.classes.DRAGOVER); this._callParentMethod('showDropZone', dropdata, event);