Proyectos de Subversion Moodle

Rev

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

{"version":3,"file":"category.min.js","sources":["../src/category.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 * The category component.\n *\n * @module     qbank_managecategories/category\n * @class      qbank_managecategories/category\n */\n\nimport {BaseComponent, DragDrop} from 'core/reactive';\nimport {categorymanager} from 'qbank_managecategories/categorymanager';\nimport Templates from 'core/templates';\nimport Modal from \"core/modal\";\nimport {get_string as getString} from \"core/str\";\n\nexport default class extends BaseComponent {\n\n    create(descriptor) {\n        this.name = descriptor.element.id;\n        this.selectors = {\n            CATEGORY_LIST: '.qbank_managecategories-categorylist',\n            CATEGORY_ITEM: '.qbank_managecategories-item[data-categoryid]',\n            CATEGORY_CONTENTS: '.qbank_managecategories-item > .container',\n            EDIT_BUTTON: '[data-action=\"addeditcategory\"]',\n            MOVE_BUTTON: '[role=\"menuitem\"][data-actiontype=\"move\"]',\n            CONTEXT: '.qbank_managecategories-categorylist[data-contextid]',\n            MODAL_CATEGORY_ITEM: '.modal_category_item[data-movingcategoryid]',\n            CONTENT_AREA: '.qbank_managecategories-details',\n            CATEGORY_ID: id => `#category-${id}`,\n            CONTENT_CONTAINER: id => `#category-${id} .qbank_managecategories-childlistcontainer`,\n            CHILD_LIST: id => `ul[data-categoryid=\"${id}\"]`,\n            PREVIOUS_SIBLING: sortorder => `:scope > [data-sortorder=\"${sortorder}\"]`,\n        };\n        this.classes = {\n            NO_BOTTOM_PADDING: 'pb-0',\n            DRAGHANDLE: 'draghandle',\n            DROPTARGET: 'qbank_managecategories-droptarget-before',\n        };\n        this.ids = {\n            CATEGORY: id => `category-${id}`,\n        };\n    }\n\n    stateReady() {\n        this.initDragDrop();\n        this.addEventListener(this.getElement(this.selectors.EDIT_BUTTON), 'click', categorymanager.showEditModal);\n        const moveButton = this.getElement(this.selectors.MOVE_BUTTON);\n        this.addEventListener(moveButton, 'click', this.showMoveModal);\n    }\n\n    destroy() {\n        // The draggable element must be unregistered.\n        this.deInitDragDrop();\n    }\n\n    /**\n     * Remove any existing DragDrop component, and create a new one.\n     */\n    initDragDrop() {\n        this.deInitDragDrop();\n        // If the element is currently draggable, register the getDraggableData method.\n        if (this.element.classList.contains(this.classes.DRAGHANDLE)) {\n            this.getDraggableData = this._getDraggableData;\n        }\n        this.dragdrop = new DragDrop(this);\n    }\n\n    /**\n     * If the DragDrop component is currently registered, unregister it.\n     */\n    deInitDragDrop() {\n        if (this.dragdrop !== undefined) {\n            if (this.getDraggableData !== undefined) {\n                this.dragdrop.setDraggable(false);\n                this.getDraggableData = undefined;\n            }\n            this.dragdrop.unregister();\n            this.dragdrop = undefined;\n        }\n    }\n\n    /**\n     * Static method to create a component instance.\n     *\n     * @param {string} target the DOM main element or its ID\n     * @param {object} selectors optional css selector overrides\n     * @return {Component}\n     */\n    static init(target, selectors) {\n        return new this({\n            element: document.querySelector(target),\n            selectors,\n            reactive: categorymanager,\n        });\n    }\n\n    /**\n     * Return the category ID from the component's element.\n     *\n     * This method is referenced as getDraggableData when the component can be dragged.\n     *\n     * @return {{id: string}}\n     * @private\n     */\n    _getDraggableData() {\n        return {\n            id: this.getElement().dataset.categoryid\n        };\n    }\n\n    validateDropData() {\n        return true;\n    }\n\n    /**\n     * Highlight the top border of the category item.\n     *\n     * @param {Object} dropData\n     */\n    showDropZone(dropData) {\n        if (this.getElement().closest(this.selectors.CATEGORY_ID(dropData.id))) {\n            // Can't drop onto itself or its own child.\n            return false;\n        }\n        this.getElement().classList.add(this.classes.DROPTARGET);\n        return true;\n    }\n\n    /**\n     * Remove highlighting.\n     */\n    hideDropZone() {\n        this.getElement().classList.remove(this.classes.DROPTARGET);\n    }\n\n    /**\n     * Find the new position of the dropped category, and trigger the move.\n     *\n     * @param {Object} dropData The category being moved.\n     * @param {Event} event The drop event.\n     */\n    drop(dropData, event) {\n        const dropTarget = event.target.closest(this.selectors.CATEGORY_ITEM);\n\n        if (!dropTarget) {\n            return;\n        }\n\n        if (dropTarget.closest(this.selectors.CATEGORY_ID(dropData.id))) {\n            // Can't drop onto your own child.\n            return;\n        }\n\n        const source = document.getElementById(this.ids.CATEGORY(dropData.id));\n\n        if (!source) {\n            return;\n        }\n\n        const targetParentId = dropTarget.dataset.parent;\n        const parentList = dropTarget.closest(this.selectors.CATEGORY_LIST);\n        let precedingSibling;\n\n        if (dropTarget === parentList.firstElementChild) {\n            // Dropped at the top of the list.\n            precedingSibling = null;\n        } else {\n            precedingSibling = dropTarget.previousElementSibling;\n        }\n\n        // Insert the category after the target category\n        categorymanager.moveCategory(dropData.id, targetParentId, precedingSibling?.dataset.categoryid);\n    }\n\n    getWatchers() {\n        return [\n            // After any update to this category, move it to the new position.\n            {watch: `categories[${this.element.dataset.categoryid}]:updated`, handler: this.updatePosition},\n            // When the template context is added or updated, re-render the content.\n            {watch: `categories[${this.element.dataset.categoryid}].templatecontext:created`, handler: this.rerender},\n            {watch: `categories[${this.element.dataset.categoryid}].templatecontext:updated`, handler: this.rerender},\n            // When a new category is created, check whether we need to add a child list to this category.\n            {watch: `categories:created`, handler: this.checkChildList},\n        ];\n    }\n\n    /**\n     * Re-render the category content.\n     *\n     * @param {Object} args\n     * @param {Element} args.element\n     * @return {Promise<Array>}\n     */\n    async rerender({element}) {\n        const {html, js} = await Templates.renderForPromise(\n            'qbank_managecategories/category_details',\n            element.templatecontext\n        );\n        return Templates.replaceNodeContents(this.getElement(this.selectors.CONTENT_AREA), html, js);\n    }\n\n    /**\n     * Render and append a new child list.\n     *\n     * @param {Object} context Template context, must include at least categoryid.\n     * @return {Promise<Element>}\n     */\n    async createChildList(context) {\n        const {html, js} = await Templates.renderForPromise(\n            'qbank_managecategories/childlist',\n            context,\n        );\n        const parentContainer = document.querySelector(this.selectors.CONTENT_CONTAINER(context.categoryid));\n        await Templates.appendNodeContents(parentContainer, html, js);\n        const childList = document.querySelector(this.selectors.CHILD_LIST(context.categoryid));\n        childList.closest(this.selectors.CATEGORY_CONTENTS).classList.add(this.classes.NO_BOTTOM_PADDING);\n        return childList;\n    }\n\n    /**\n     * Move a category to its new position.\n     *\n     * A category may change its parent, sortorder and draghandle independently or at the same time. This method will resolve those\n     * changes and move the element to the new position. If the parent doesn't already have a child list, one will be created.\n     *\n     * If the parent has changed, this will also update the state with the new child count of the old and new parents.\n     *\n     * @param {Object} args\n     * @param {Object} args.element\n     * @return {Promise<void>}\n     */\n    async updatePosition({element}) {\n        // Move to a new parent category.\n        let newParent;\n        const originParent = document.querySelector(this.selectors.CHILD_LIST(this.getElement().dataset.parent));\n        if (parseInt(this.getElement().dataset.parent) !== element.parent) {\n            newParent = document.querySelector(this.selectors.CHILD_LIST(element.parent));\n            if (!newParent) {\n                // The target category doesn't have a child list yet. We'd better create one.\n                newParent = await this.createChildList({categoryid: element.parent});\n            }\n            this.getElement().dataset.parent = element.parent;\n        } else {\n            newParent = this.getElement().parentElement;\n        }\n\n        // Move to a new position within the parent.\n        let previousSibling;\n        let nextSibling;\n        if (newParent.firstElementChild && parseInt(element.sortorder) <= parseInt(newParent.firstElementChild.dataset.sortorder)) {\n            // Move to the top of the list.\n            nextSibling = newParent.firstElementChild;\n        } else {\n            // Move later in the list.\n            previousSibling = newParent.querySelector(this.selectors.PREVIOUS_SIBLING(element.sortorder - 1));\n            nextSibling = previousSibling?.nextElementSibling;\n        }\n\n        // Check if this has actually moved, or if it's just having its sortorder updated due to another element moving.\n        const moved = (newParent !== this.getElement().parentElement || nextSibling !== this.getElement());\n\n        if (moved) {\n            if (nextSibling) {\n                // Move to the specified position in the list.\n                newParent.insertBefore(this.getElement(), nextSibling);\n            } else {\n                // Move to the end of the list (may also be the top of the list is empty).\n                newParent.appendChild(this.getElement());\n            }\n        }\n        if (originParent !== newParent) {\n            // Update child count of old and new parent.\n            this.reactive.stateManager.processUpdates([\n                {\n                    name: 'categoryLists',\n                    action: 'put',\n                    fields: {\n                        id: originParent.dataset.categoryid,\n                        childCount: originParent.querySelectorAll(this.selectors.CATEGORY_ITEM).length\n                    }\n                },\n                {\n                    name: 'categoryLists',\n                    action: 'put',\n                    fields: {\n                        id: newParent.dataset.categoryid,\n                        childCount: newParent.querySelectorAll(this.selectors.CATEGORY_ITEM).length\n                    }\n                }\n            ]);\n        }\n\n        this.element.dataset.sortorder = element.sortorder;\n\n        // Enable/disable dragging.\n        const isDraggable = this.element.classList.contains(this.classes.DRAGHANDLE);\n        if (isDraggable && !element.draghandle) {\n            this.element.classList.remove(this.classes.DRAGHANDLE);\n            this.initDragDrop();\n        } else if (!isDraggable && element.draghandle) {\n            this.element.classList.add(this.classes.DRAGHANDLE);\n            this.initDragDrop();\n        }\n    }\n\n    /**\n     * Recursively create a list of all valid destinations for a current category within a parent category.\n     *\n     * @param {Element} item\n     * @param {Number} movingCategoryId\n     * @return {Array<Object>}\n     */\n    createMoveCategoryList(item, movingCategoryId) {\n        const categories = [];\n        if (item.children) {\n            let precedingSibling = null;\n            item.children.forEach(category => {\n                const categoryId = parseInt(category.dataset.categoryid);\n                // Don't create a target for the category that's moving.\n                if (categoryId === movingCategoryId) {\n                    return;\n                }\n                // Create a target to move before this child.\n                let child = {\n                    categoryid: categoryId,\n                    movingcategoryid: movingCategoryId,\n                    precedingsiblingid: precedingSibling?.dataset.categoryid ?? 0,\n                    parent: category.dataset.parent,\n                    categoryname: category.dataset.categoryname,\n                    categories: null,\n                    current: categoryId === movingCategoryId,\n                };\n                const childList = category.querySelector(this.selectors.CATEGORY_LIST);\n                if (childList) {\n                    // If the child has its own children, recursively make a list of those.\n                    child.categories = this.createMoveCategoryList(childList, movingCategoryId);\n                } else {\n                    // Otherwise, create a target to move as a new child of this one.\n                    child.categories = [\n                        {\n                            movingcategoryid: movingCategoryId,\n                            precedingsiblingid: 0,\n                            parent: categoryId,\n                            categoryname: category.dataset.categoryname,\n                            categories: null,\n                            newchild: true,\n                        }\n                    ];\n                }\n                categories.push(child);\n                precedingSibling = category;\n            });\n            if (precedingSibling) {\n                const precedingId = parseInt(precedingSibling.dataset.categoryid);\n                if (precedingId !== movingCategoryId) {\n                    // If this is the last child of its parent, also create a target to move the category after this one.\n                    categories.push({\n                        movingcategoryid: movingCategoryId,\n                        precedingsiblingid: precedingId,\n                        parent: precedingSibling.dataset.parent,\n                        categoryname: precedingSibling.dataset.categoryname,\n                        categories: null,\n                        lastchild: true,\n                    });\n                }\n            }\n        }\n        return categories;\n    }\n\n    /**\n     * Displays a modal containing links to move the category to a new location.\n     *\n     * @param {Event} e Button click event.\n     */\n    async showMoveModal(e) {\n        // Return if it is not menu item.\n        const item = e.target.closest(this.selectors.MOVE_BUTTON);\n        if (!item) {\n            return;\n        }\n        // Return if it is disabled.\n        if (item.getAttribute('aria-disabled') === 'true') {\n            return;\n        }\n\n        // Prevent addition click on the item.\n        item.setAttribute('aria-disabled', true);\n\n        // Build the list of move links.\n        let moveList = {contexts: []};\n        const contexts = document.querySelectorAll(this.selectors.CONTEXT);\n        contexts.forEach(context => {\n            const moveContext = {\n                contextname: context.dataset.contextname,\n                categories: [],\n                hascategories: false,\n            };\n            moveContext.categories = this.createMoveCategoryList(context, parseInt(item.dataset.categoryid));\n            moveContext.hascategories = moveContext.categories.length > 0;\n            moveList.contexts.push(moveContext);\n        });\n\n        const modal = await Modal.create({\n            title: getString('movecategory', 'qbank_managecategories', item.dataset.categoryname),\n            body: Templates.render('qbank_managecategories/move_context_list', moveList),\n            footer: '',\n            show: true,\n            large: true,\n        });\n        // Show modal and add click event for list items.\n        modal.getBody()[0].addEventListener('click', e => {\n            const target = e.target.closest(this.selectors.MODAL_CATEGORY_ITEM);\n            if (!target) {\n                return;\n            }\n            categorymanager.moveCategory(target.dataset.movingcategoryid, target.dataset.parent, target.dataset.precedingsiblingid);\n            modal.destroy();\n        });\n        item.setAttribute('aria-disabled', false);\n    }\n\n    /**\n     * Check and add a child list if needed.\n     *\n     * Check whether the category that has just been added has this category as its parent. If it does,\n     * check that this category has a child list, and if not, add one.\n     *\n     * @param {Object} args\n     * @param {Element} args.element The new category.\n     * @return {Promise<Element>}\n     */\n    async checkChildList({element}) {\n        if (element.parent !== this.getElement().dataset.categoryid) {\n            return null; // Not for me.\n        }\n        let childList = this.getElement(this.selectors.CATEGORY_LIST);\n        if (childList) {\n            return null; // List already exists, it will handle adding the new category.\n        }\n        // Render and add a new child list containing the new category.\n        return this.createChildList({\n            categoryid: element.parent,\n            children: [\n                element.templatecontext,\n            ]\n        });\n    }\n}\n"],"names":["BaseComponent","create","descriptor","name","element","id","selectors","CATEGORY_LIST","CATEGORY_ITEM","CATEGORY_CONTENTS","EDIT_BUTTON","MOVE_BUTTON","CONTEXT","MODAL_CATEGORY_ITEM","CONTENT_AREA","CATEGORY_ID","CONTENT_CONTAINER","CHILD_LIST","PREVIOUS_SIBLING","sortorder","classes","NO_BOTTOM_PADDING","DRAGHANDLE","DROPTARGET","ids","CATEGORY","stateReady","initDragDrop","addEventListener","this","getElement","categorymanager","showEditModal","moveButton","showMoveModal","destroy","deInitDragDrop","classList","contains","getDraggableData","_getDraggableData","dragdrop","DragDrop","undefined","setDraggable","unregister","target","document","querySelector","reactive","dataset","categoryid","validateDropData","showDropZone","dropData","closest","add","hideDropZone","remove","drop","event","dropTarget","getElementById","targetParentId","parent","parentList","precedingSibling","firstElementChild","previousElementSibling","moveCategory","_precedingSibling","getWatchers","watch","handler","updatePosition","rerender","checkChildList","html","js","Templates","renderForPromise","templatecontext","replaceNodeContents","context","parentContainer","appendNodeContents","childList","newParent","originParent","previousSibling","nextSibling","parseInt","createChildList","parentElement","_previousSibling","nextElementSibling","insertBefore","appendChild","stateManager","processUpdates","action","fields","childCount","querySelectorAll","length","isDraggable","draghandle","createMoveCategoryList","item","movingCategoryId","categories","children","forEach","category","categoryId","child","movingcategoryid","precedingsiblingid","_precedingSibling2","categoryname","current","newchild","push","precedingId","lastchild","e","getAttribute","setAttribute","moveList","contexts","moveContext","contextname","hascategories","modal","Modal","title","body","render","footer","show","large","getBody"],"mappings":"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"}