Proyectos de Subversion Moodle

Rev

Rev 1 | Autoría | Comparar con el anterior | Ultima modificación | Ver Log |

{"version":3,"file":"bulk_actions.min.js","sources":["../../src/bulkactions/bulk_actions.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\nimport Templates from 'core/templates';\nimport {get_string as getString} from 'core/str';\nimport {disableStickyFooter, enableStickyFooter} from 'core/sticky-footer';\n\n/**\n * Base class for defining a bulk actions area within a page.\n *\n * @module     core/bulkactions/bulk_actions\n * @copyright  2023 Mihail Geshoski <mihail@moodle.com>\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/** @constant {Object} The object containing the relevant selectors. */\nconst Selectors = {\n    stickyFooterContainer: '#sticky-footer',\n    selectedItemsCountContainer: '[data-type=\"bulkactions\"] [data-for=\"bulkcount\"]',\n    cancelBulkActionModeElement: '[data-type=\"bulkactions\"] [data-action=\"bulkcancel\"]',\n    bulkModeContainer: '[data-type=\"bulkactions\"]',\n    bulkActionsContainer: '[data-type=\"bulkactions\"] [data-for=\"bulktools\"]'\n};\n\nexport default class BulkActions {\n\n    /** @property {string|null} initialStickyFooterContent The initial content of the sticky footer. */\n    initialStickyFooterContent = null;\n\n    /** @property {Array} selectedItems The array of selected item elements. */\n    selectedItems = [];\n\n    /** @property {boolean} isBulkActionsModeEnabled Whether the bulk actions mode is enabled. */\n    isBulkActionsModeEnabled = false;\n\n    /**\n     * @property {int} maxButtons Sets the maximum number of action buttons to display. If exceeded, additional actions\n     *                            are shown in a dropdown menu.\n     */\n    maxButtons = 5;\n\n    /**\n     * The class constructor.\n     *\n     * @param {int|null} maxButtons Sets the maximum number of action buttons to display. If exceeded, additional actions\n     *                              are shown in a dropdown menu.\n     * @returns {void}\n     */\n    constructor(maxButtons = null) {\n        if (!this.getStickyFooterContainer()) {\n            throw new Error('Sticky footer not found.');\n        }\n        // Store any pre-existing content in the sticky footer. When bulk actions mode is enabled, this content will be\n        // replaced with the bulk actions content and restored when bulk actions mode is disabled.\n        this.initialStickyFooterContent = this.getStickyFooterContainer().innerHTML;\n        if (maxButtons) {\n            this.maxButtons = maxButtons;\n        }\n        // Register and handle the item select change event.\n        this.registerItemSelectChangeEvent(async() => {\n            this.selectedItems = this.getSelectedItems();\n            if (this.selectedItems.length > 0) { // At least one item is selected.\n                // If the bulk actions mode is already enabled only update the selected items count.\n                if (this.isBulkActionsModeEnabled) {\n                    await this.updateBulkItemSelection();\n                } else { // Otherwise, enable the bulk action mode.\n                    await this.enableBulkActionsMode();\n                }\n            } else { // No items are selected, disable the bulk action mode.\n                this.disableBulkActionsMode();\n            }\n        });\n    }\n\n    /**\n     * Returns the array of the relevant bulk action objects.\n     *\n     * @method getBulkActions\n     * @returns {Array}\n     */\n    getBulkActions() {\n        throw new Error(`getBulkActions() must be implemented in ${this.constructor.name}`);\n    }\n\n    /**\n     * Returns the array of selected items.\n     *\n     * @method getSelectedItems\n     * @returns {Array}\n     */\n    getSelectedItems() {\n        throw new Error(`getSelectedItems() must be implemented in ${this.constructor.name}`);\n    }\n\n    /**\n     * Adds the listener for the item select change event.\n     * The event handler function that is passed as a parameter should be called right after the event is triggered.\n     *\n     * @method registerItemSelectChangeEvent\n     * @param {function} eventHandler The event handler function.\n     * @returns {void}\n     */\n    registerItemSelectChangeEvent(eventHandler) {\n        throw new Error(`registerItemSelectChangeEvent(${eventHandler}) must be implemented in ${this.constructor.name}`);\n    }\n\n    /**\n     * Defines the action for deselecting a selected item.\n     *\n     * The base bulk actions class supports deselecting all selected items but does not have knowledge of the type of the\n     * selected element. Therefore, each subclass must explicitly define the action of resetting the attributes that\n     * indicate a selected state.\n     *\n     * @method deselectItem\n     * @param {HTMLElement} selectedItem The selected element.\n     * @returns {void}\n     */\n    deselectItem(selectedItem) {\n        throw new Error(`deselectItem(${selectedItem}) must be implemented in ${this.constructor.name}`);\n    }\n\n    /**\n     * Returns the sticky footer container.\n     *\n     * @method getStickyFooterContainer\n     * @returns {HTMLElement}\n     */\n    getStickyFooterContainer() {\n        return document.querySelector(Selectors.stickyFooterContainer);\n    }\n\n    /**\n     * Enables the bulk action mode.\n     *\n     * @method enableBulkActionsMode\n     * @returns {Promise<void>}\n     */\n    async enableBulkActionsMode() {\n        // Make sure that the sticky footer is enabled.\n        enableStickyFooter();\n        // Render the bulk actions content in the sticky footer container.\n        this.getStickyFooterContainer().innerHTML = await this.renderBulkActions();\n        const bulkModeContainer = this.getStickyFooterContainer().querySelector(Selectors.bulkModeContainer);\n        const bulkActionsContainer = bulkModeContainer.querySelector(Selectors.bulkActionsContainer);\n        this.getBulkActions().forEach((bulkAction) => {\n            // Register the listener events for each available bulk action.\n            bulkAction.registerListenerEvents(bulkActionsContainer);\n            // Set the selected items for each available bulk action.\n            bulkAction.setSelectedItems(this.selectedItems);\n        });\n        // Register the click listener event for the cancel bulk mode button.\n        bulkModeContainer.addEventListener('click', (e) => {\n            if (e.target.closest(Selectors.cancelBulkActionModeElement)) {\n                // Deselect all selected items.\n                this.selectedItems.forEach((item) => {\n                    this.deselectItem(item);\n                });\n                // Disable the bulk action mode.\n                this.disableBulkActionsMode();\n            }\n        });\n        this.isBulkActionsModeEnabled = true;\n\n        // Calling `renderBulkActions()` already renders the item selection count.\n        // Because of this, screen readers will not announce the item selection count on first load given that aria-live regions\n        // must be present in the DOM and have their contents changed before screen readers can announce their content.\n        // So, we call `updateBulkItemSelection()` after a short delay to ensure that screen readers announce the item count.\n        setTimeout(async() => {\n            await this.updateBulkItemSelection();\n        }, 300);\n    }\n\n    /**\n     * Disables the bulk action mode.\n     *\n     * @method disableBulkActionsMode\n     * @returns {void}\n     */\n    disableBulkActionsMode() {\n        // If there was any previous (initial) content in the sticky footer, restore it.\n        if (this.initialStickyFooterContent.length > 0) {\n            this.getStickyFooterContainer().innerHTML = this.initialStickyFooterContent;\n        } else { // No previous content to restore, disable the sticky footer.\n            disableStickyFooter();\n        }\n        this.isBulkActionsModeEnabled = false;\n    }\n\n    /**\n     * Renders the bulk actions content.\n     *\n     * @method renderBulkActions\n     * @returns {Promise<string>}\n     */\n    async renderBulkActions() {\n        const data = {\n            bulkselectioncount: this.selectedItems.length,\n            actions: [],\n            moreactions: [],\n            hasmoreactions: false,\n        };\n        const bulkActions = this.getBulkActions();\n        const showMoreButton = bulkActions.length > this.maxButtons;\n\n        // Get all bulk actions and render them in order.\n        const actions = await Promise.all(\n            bulkActions.map((bulkAction, index) =>\n                bulkAction.renderBulkActionTrigger(\n                    showMoreButton && (index >= this.maxButtons - 1),\n                    index\n                )\n            )\n        );\n\n        // Separate rendered actions into data.actions and data.moreactions in the correct order.\n        actions.forEach((actionTrigger, index) => {\n            if (showMoreButton && (index >= this.maxButtons - 1)) {\n                data.moreactions.push({'actiontrigger': actionTrigger});\n            } else {\n                data.actions.push({'actiontrigger': actionTrigger});\n            }\n        });\n\n        data.hasmoreactions = data.moreactions.length > 0;\n\n        return Templates.render('core/bulkactions/bulk_actions', data);\n    }\n\n    /**\n     * Updates the selected items count in the bulk actions content.\n     *\n     * @method updateBulkItemSelection\n     * @returns {void}\n     */\n    async updateBulkItemSelection() {\n        const bulkSelection = await getString('bulkselection', 'core', this.selectedItems.length);\n        document.querySelector(Selectors.selectedItemsCountContainer).innerHTML = bulkSelection;\n    }\n}\n"],"names":["Selectors","constructor","maxButtons","this","getStickyFooterContainer","Error","initialStickyFooterContent","innerHTML","registerItemSelectChangeEvent","async","selectedItems","getSelectedItems","length","isBulkActionsModeEnabled","updateBulkItemSelection","enableBulkActionsMode","disableBulkActionsMode","getBulkActions","name","eventHandler","deselectItem","selectedItem","document","querySelector","renderBulkActions","bulkModeContainer","bulkActionsContainer","forEach","bulkAction","registerListenerEvents","setSelectedItems","addEventListener","e","target","closest","item","setTimeout","data","bulkselectioncount","actions","moreactions","hasmoreactions","bulkActions","showMoreButton","Promise","all","map","index","renderBulkActionTrigger","actionTrigger","push","Templates","render","bulkSelection"],"mappings":";;;;;;;yJA4BMA,gCACqB,iBADrBA,sCAE2B,mDAF3BA,sCAG2B,uDAH3BA,4BAIiB,4BAJjBA,+BAKoB,iFA2BtBC,kBAAYC,kEAAa,0DArBI,2CAGb,qDAGW,qCAMd,IAUJC,KAAKC,iCACA,IAAIC,MAAM,iCAIfC,2BAA6BH,KAAKC,2BAA2BG,UAC9DL,kBACKA,WAAaA,iBAGjBM,+BAA8BC,eAC1BC,cAAgBP,KAAKQ,mBACtBR,KAAKO,cAAcE,OAAS,EAExBT,KAAKU,+BACCV,KAAKW,gCAELX,KAAKY,6BAGVC,4BAWjBC,uBACU,IAAIZ,wDAAiDF,KAAKF,YAAYiB,OAShFP,yBACU,IAAIN,0DAAmDF,KAAKF,YAAYiB,OAWlFV,8BAA8BW,oBACpB,IAAId,8CAAuCc,iDAAwChB,KAAKF,YAAYiB,OAc9GE,aAAaC,oBACH,IAAIhB,6BAAsBgB,iDAAwClB,KAAKF,YAAYiB,OAS7Fd,kCACWkB,SAASC,cAAcvB,2GAazBI,2BAA2BG,gBAAkBJ,KAAKqB,0BACjDC,kBAAoBtB,KAAKC,2BAA2BmB,cAAcvB,6BAClE0B,qBAAuBD,kBAAkBF,cAAcvB,qCACxDiB,iBAAiBU,SAASC,aAE3BA,WAAWC,uBAAuBH,sBAElCE,WAAWE,iBAAiB3B,KAAKO,kBAGrCe,kBAAkBM,iBAAiB,SAAUC,IACrCA,EAAEC,OAAOC,QAAQlC,8CAEZU,cAAciB,SAASQ,YACnBf,aAAae,cAGjBnB,kCAGRH,0BAA2B,EAMhCuB,YAAW3B,gBACDN,KAAKW,4BACZ,KASPE,yBAEQb,KAAKG,2BAA2BM,OAAS,OACpCR,2BAA2BG,UAAYJ,KAAKG,wEAIhDO,0BAA2B,kCAU1BwB,KAAO,CACTC,mBAAoBnC,KAAKO,cAAcE,OACvC2B,QAAS,GACTC,YAAa,GACbC,gBAAgB,GAEdC,YAAcvC,KAAKc,iBACnB0B,eAAiBD,YAAY9B,OAAST,KAAKD,wBAG3B0C,QAAQC,IAC1BH,YAAYI,KAAI,CAAClB,WAAYmB,QACzBnB,WAAWoB,wBACPL,gBAAmBI,OAAS5C,KAAKD,WAAa,EAC9C6C,WAMJpB,SAAQ,CAACsB,cAAeF,SACxBJ,gBAAmBI,OAAS5C,KAAKD,WAAa,EAC9CmC,KAAKG,YAAYU,KAAK,eAAkBD,gBAExCZ,KAAKE,QAAQW,KAAK,eAAkBD,mBAI5CZ,KAAKI,eAAiBJ,KAAKG,YAAY5B,OAAS,EAEzCuC,mBAAUC,OAAO,gCAAiCf,4CAUnDgB,oBAAsB,mBAAU,gBAAiB,OAAQlD,KAAKO,cAAcE,QAClFU,SAASC,cAAcvB,uCAAuCO,UAAY8C"}