Proyectos de Subversion Moodle

Rev

Autoría | Ultima modificación | Ver Log |

{"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 <http://www.gnu.org/licenses/>.\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 <ferran@moodle.com>\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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"}