Proyectos de Subversion Moodle

Rev

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

{"version":3,"file":"grade.min.js","sources":["../../src/comboboxsearch/grade.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 * Allow the user to search for grades within the grade area.\n *\n * @module    core_grades/comboboxsearch/grade\n * @copyright 2023 Mathew May <mathew.solutions>\n * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport search_combobox from 'core/comboboxsearch/search_combobox';\nimport * as Repository from 'core_grades/searchwidget/repository';\nimport {renderForPromise, replaceNodeContents} from 'core/templates';\nimport {debounce} from 'core/utils';\n\nexport default class GradeItemSearch extends search_combobox {\n\n    courseID;\n\n    constructor() {\n        super();\n\n        // Define our standard lookups.\n        this.selectors = {\n            ...this.selectors,\n            courseid: '[data-region=\"courseid\"]',\n            placeholder: '.gradesearchdropdown [data-region=\"searchplaceholder\"]',\n        };\n        const component = document.querySelector(this.componentSelector());\n        this.courseID = component.querySelector(this.selectors.courseid).dataset.courseid;\n        this.instance = this.component.querySelector(this.selectors.instance).dataset.instance;\n\n        const searchValueElement = this.component.querySelector(`#${this.searchInput.dataset.inputElement}`);\n        searchValueElement.addEventListener('change', () => {\n            this.toggleDropdown(); // Otherwise the dropdown stays open when user choose an option using keyboard.\n\n            const valueElement = this.component.querySelector(`#${this.combobox.dataset.inputElement}`);\n            if (valueElement.value !== searchValueElement.value) {\n                valueElement.value = searchValueElement.value;\n                valueElement.dispatchEvent(new Event('change', {bubbles: true}));\n            }\n\n            searchValueElement.value = '';\n        });\n\n        this.$component.on('hide.bs.dropdown', () => {\n            this.searchInput.removeAttribute('aria-activedescendant');\n\n            const listbox = document.querySelector(`#${this.searchInput.getAttribute('aria-controls')}[role=\"listbox\"]`);\n            listbox.querySelectorAll('.active[role=\"option\"]').forEach(option => {\n                option.classList.remove('active');\n            });\n            listbox.scrollTop = 0;\n\n            // Use setTimeout to make sure the following code is executed after the click event is handled.\n            setTimeout(() => {\n                if (this.searchInput.value !== '') {\n                    this.searchInput.value = '';\n                    this.searchInput.dispatchEvent(new Event('input', {bubbles: true}));\n                }\n            });\n        });\n\n        this.renderDefault();\n    }\n\n    static init() {\n        return new GradeItemSearch();\n    }\n\n    /**\n     * The overall div that contains the searching widget.\n     *\n     * @returns {string}\n     */\n    componentSelector() {\n        return '.grade-search';\n    }\n\n    /**\n     * The dropdown div that contains the searching widget result space.\n     *\n     * @returns {string}\n     */\n    dropdownSelector() {\n        return '.gradesearchdropdown';\n    }\n\n    /**\n     * Build the content then replace the node.\n     */\n    async renderDropdown() {\n        const {html, js} = await renderForPromise('core/local/comboboxsearch/resultset', {\n            instance: this.instance,\n            results: this.getMatchedResults(),\n            hasresults: this.getMatchedResults().length > 0,\n            searchterm: this.getSearchTerm(),\n        });\n        replaceNodeContents(this.selectors.placeholder, html, js);\n        // Remove aria-activedescendant when the available options change.\n        this.searchInput.removeAttribute('aria-activedescendant');\n    }\n\n    /**\n     * Build the content then replace the node by default we want our form to exist.\n     */\n    async renderDefault() {\n        this.setMatchedResults(await this.filterDataset(await this.getDataset()));\n        this.filterMatchDataset();\n\n        await this.renderDropdown();\n\n        this.updateNodes();\n        this.registerInputEvents();\n    }\n\n    /**\n     * Get the data we will be searching against in this component.\n     *\n     * @returns {Promise<*>}\n     */\n    async fetchDataset() {\n        return await Repository.gradeitemFetch(this.courseID).then((r) => r.gradeitems);\n    }\n\n    /**\n     * Dictate to the search component how and what we want to match upon.\n     *\n     * @param {Array} filterableData\n     * @returns {Array} The users that match the given criteria.\n     */\n    async filterDataset(filterableData) {\n        // Sometimes we just want to show everything.\n        if (this.getPreppedSearchTerm() === '') {\n            return filterableData;\n        }\n        return filterableData.filter((grade) => Object.keys(grade).some((key) => {\n            if (grade[key] === \"\") {\n                return false;\n            }\n            return grade[key].toString().toLowerCase().includes(this.getPreppedSearchTerm());\n        }));\n    }\n\n    /**\n     * Given we have a subset of the dataset, set the field that we matched upon to inform the end user.\n     */\n    filterMatchDataset() {\n        this.setMatchedResults(\n            this.getMatchedResults().map((grade) => {\n                return {\n                    id: grade.id,\n                    name: grade.name,\n                };\n            })\n        );\n    }\n\n    /**\n     * Handle any keyboard inputs.\n     */\n    registerInputEvents() {\n        // Register & handle the text input.\n        this.searchInput.addEventListener('input', debounce(async() => {\n            this.setSearchTerms(this.searchInput.value);\n            // We can also require a set amount of input before search.\n            if (this.searchInput.value === '') {\n                // Hide the \"clear\" search button in the search bar.\n                this.clearSearchButton.classList.add('d-none');\n            } else {\n                // Display the \"clear\" search button in the search bar.\n                this.clearSearchButton.classList.remove('d-none');\n            }\n            // User has given something for us to filter against.\n            await this.filterrenderpipe();\n        }, 300));\n    }\n\n    /**\n     * The handler for when a user interacts with the component.\n     *\n     * @param {MouseEvent} e The triggering event that we are working with.\n     */\n    async clickHandler(e) {\n        if (e.target.closest(this.selectors.clearSearch)) {\n            e.stopPropagation();\n            // Clear the entered search query in the search bar.\n            this.searchInput.value = '';\n            this.setSearchTerms(this.searchInput.value);\n            this.searchInput.focus();\n            this.clearSearchButton.classList.add('d-none');\n            // Display results.\n            await this.filterrenderpipe();\n        }\n    }\n\n    /**\n     * The handler for when a user changes the value of the component (selects an option from the dropdown).\n     *\n     * @param {Event} e The change event.\n     */\n    changeHandler(e) {\n        window.location = this.selectOneLink(e.target.value);\n    }\n\n    /**\n     * Override the input event listener for the text input area.\n     */\n    registerInputHandlers() {\n        // Register & handle the text input.\n        this.searchInput.addEventListener('input', debounce(() => {\n            this.setSearchTerms(this.searchInput.value);\n            // We can also require a set amount of input before search.\n            if (this.getSearchTerm() === '') {\n                // Hide the \"clear\" search button in the search bar.\n                this.clearSearchButton.classList.add('d-none');\n            } else {\n                // Display the \"clear\" search button in the search bar.\n                this.clearSearchButton.classList.remove('d-none');\n            }\n        }, 300));\n    }\n\n    /**\n     * Build up the view all link that is dedicated to a particular result.\n     * We will call this function when a user interacts with the combobox to redirect them to show their results in the page.\n     *\n     * @param {Number} gradeID The ID of the grade item selected.\n     */\n    selectOneLink(gradeID) {\n        throw new Error(`selectOneLink(${gradeID}) must be implemented in ${this.constructor.name}`);\n    }\n}\n"],"names":["GradeItemSearch","search_combobox","constructor","selectors","this","courseid","placeholder","component","document","querySelector","componentSelector","courseID","dataset","instance","searchValueElement","searchInput","inputElement","addEventListener","toggleDropdown","valueElement","combobox","value","dispatchEvent","Event","bubbles","$component","on","removeAttribute","listbox","getAttribute","querySelectorAll","forEach","option","classList","remove","scrollTop","setTimeout","renderDefault","dropdownSelector","html","js","results","getMatchedResults","hasresults","length","searchterm","getSearchTerm","setMatchedResults","filterDataset","getDataset","filterMatchDataset","renderDropdown","updateNodes","registerInputEvents","Repository","gradeitemFetch","then","r","gradeitems","filterableData","getPreppedSearchTerm","filter","grade","Object","keys","some","key","toString","toLowerCase","includes","map","id","name","async","setSearchTerms","clearSearchButton","add","filterrenderpipe","e","target","closest","clearSearch","stopPropagation","focus","changeHandler","window","location","selectOneLink","registerInputHandlers","gradeID","Error"],"mappings":"i0CA2BqBA,wBAAwBC,yBAIzCC,6LAISC,UAAY,IACVC,KAAKD,UACRE,SAAU,2BACVC,YAAa,gEAEXC,UAAYC,SAASC,cAAcL,KAAKM,0BACzCC,SAAWJ,UAAUE,cAAcL,KAAKD,UAAUE,UAAUO,QAAQP,cACpEQ,SAAWT,KAAKG,UAAUE,cAAcL,KAAKD,UAAUU,UAAUD,QAAQC,eAExEC,mBAAqBV,KAAKG,UAAUE,yBAAkBL,KAAKW,YAAYH,QAAQI,eACrFF,mBAAmBG,iBAAiB,UAAU,UACrCC,uBAECC,aAAef,KAAKG,UAAUE,yBAAkBL,KAAKgB,SAASR,QAAQI,eACxEG,aAAaE,QAAUP,mBAAmBO,QAC1CF,aAAaE,MAAQP,mBAAmBO,MACxCF,aAAaG,cAAc,IAAIC,MAAM,SAAU,CAACC,SAAS,MAG7DV,mBAAmBO,MAAQ,WAG1BI,WAAWC,GAAG,oBAAoB,UAC9BX,YAAYY,gBAAgB,+BAE3BC,QAAUpB,SAASC,yBAAkBL,KAAKW,YAAYc,aAAa,sCACzED,QAAQE,iBAAiB,0BAA0BC,SAAQC,SACvDA,OAAOC,UAAUC,OAAO,aAE5BN,QAAQO,UAAY,EAGpBC,YAAW,KACwB,KAA3BhC,KAAKW,YAAYM,aACZN,YAAYM,MAAQ,QACpBN,YAAYO,cAAc,IAAIC,MAAM,QAAS,CAACC,SAAS,iBAKnEa,qCAIE,IAAIrC,gBAQfU,0BACW,gBAQX4B,yBACW,oDAODC,KAACA,KAADC,GAAOA,UAAY,+BAAiB,sCAAuC,CAC7E3B,SAAUT,KAAKS,SACf4B,QAASrC,KAAKsC,oBACdC,WAAYvC,KAAKsC,oBAAoBE,OAAS,EAC9CC,WAAYzC,KAAK0C,qDAED1C,KAAKD,UAAUG,YAAaiC,KAAMC,SAEjDzB,YAAYY,gBAAgB,oDAO5BoB,wBAAwB3C,KAAK4C,oBAAoB5C,KAAK6C,oBACtDC,2BAEC9C,KAAK+C,sBAENC,mBACAC,wDASQC,WAAWC,eAAenD,KAAKO,UAAU6C,MAAMC,GAAMA,EAAEC,iCASpDC,sBAEoB,KAAhCvD,KAAKwD,uBACED,eAEJA,eAAeE,QAAQC,OAAUC,OAAOC,KAAKF,OAAOG,MAAMC,KAC1C,KAAfJ,MAAMI,MAGHJ,MAAMI,KAAKC,WAAWC,cAAcC,SAASjE,KAAKwD,4BAOjEV,0BACSH,kBACD3C,KAAKsC,oBAAoB4B,KAAKR,QACnB,CACHS,GAAIT,MAAMS,GACVC,KAAMV,MAAMU,UAS5BnB,2BAEStC,YAAYE,iBAAiB,SAAS,oBAASwD,eAC3CC,eAAetE,KAAKW,YAAYM,OAEN,KAA3BjB,KAAKW,YAAYM,WAEZsD,kBAAkB1C,UAAU2C,IAAI,eAGhCD,kBAAkB1C,UAAUC,OAAO,gBAGtC9B,KAAKyE,qBACZ,yBAQYC,GACXA,EAAEC,OAAOC,QAAQ5E,KAAKD,UAAU8E,eAChCH,EAAEI,uBAEGnE,YAAYM,MAAQ,QACpBqD,eAAetE,KAAKW,YAAYM,YAChCN,YAAYoE,aACZR,kBAAkB1C,UAAU2C,IAAI,gBAE/BxE,KAAKyE,oBASnBO,cAAcN,GACVO,OAAOC,SAAWlF,KAAKmF,cAAcT,EAAEC,OAAO1D,OAMlDmE,6BAESzE,YAAYE,iBAAiB,SAAS,oBAAS,UAC3CyD,eAAetE,KAAKW,YAAYM,OAER,KAAzBjB,KAAK0C,qBAEA6B,kBAAkB1C,UAAU2C,IAAI,eAGhCD,kBAAkB1C,UAAUC,OAAO,YAE7C,MASPqD,cAAcE,eACJ,IAAIC,8BAAuBD,4CAAmCrF,KAAKF,YAAYsE"}