diff --git a/grade/amd/build/searchwidget/basewidget.min.js b/grade/amd/build/searchwidget/basewidget.min.js index 038154053e9..64fbb503d1a 100644 --- a/grade/amd/build/searchwidget/basewidget.min.js +++ b/grade/amd/build/searchwidget/basewidget.min.js @@ -1,10 +1,10 @@ -define("core_grades/searchwidget/basewidget",["exports","core/modal_factory","core/modal_events","core/utils","core/templates","core/aria"],(function(_exports,ModalFactory,ModalEvents,_utils,Templates,_aria){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj} +define("core_grades/searchwidget/basewidget",["exports","core/utils","core/templates","core_grades/searchwidget/selectors","core/notification"],(function(_exports,_utils,Templates,Selectors,_notification){var obj;function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj} /** - * A small modal to search users or grade items within the gradebook. + * A widget to search users or grade items within the gradebook. * * @module core_grades/searchwidget/basewidget * @copyright 2022 Mathew May * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.promisesAndResolvers=_exports.init=void 0,ModalFactory=_interopRequireWildcard(ModalFactory),ModalEvents=_interopRequireWildcard(ModalEvents),Templates=_interopRequireWildcard(Templates);_exports.init=function(bodyPromise,data,searchFunc,modalTitle){let unsearchableContent=arguments.length>4&&void 0!==arguments[4]?arguments[4]:null;const modal=buildModal(bodyPromise,modalTitle);registerListenerEvents(modal,data,searchFunc,unsearchableContent)};const registerListenerEvents=(modal,data,searchFunc,unsearchableContent)=>{modal.then((modal=>{modal.getRoot().on(ModalEvents.hidden,(()=>{modal.destroy()})),modal.getBodyPromise().then((body=>body[0])).then((body=>{const searchInput=body.querySelector('input[data-action="search"]'),searchResultsContainer=body.querySelector('[data-region="search-results-container-widget"]');if(renderSearchResults(searchResultsContainer,data),unsearchableContent){body.querySelector('[data-region="unsearchable-content-container-widget"]').innerHTML+=unsearchableContent}return searchInput.addEventListener("input",(0,_utils.debounce)((async()=>{await renderSearchResults(searchResultsContainer,debounceCallee(searchInput.value,data,searchFunc()))}),300)),(0,_aria.comboBox)(searchInput),body})).catch()})).catch()},buildModal=(bodyPromise,modalTitle)=>ModalFactory.create({type:ModalFactory.types.DEFAULT,title:modalTitle,body:bodyPromise,small:!0,scrollable:!1,templateContext:{classes:"reportdatasearch modal-sm"}}).then((modal=>(modal.show(),modal))),debounceCallee=(searchValue,data,searchFunction)=>searchValue.length>0?searchFunction(data,searchValue):data,renderSearchResults=async(searchResultsContainer,searchResultsData)=>{const templateData={searchresults:searchResultsData},{html:html,js:js}=await Templates.renderForPromise("core_grades/searchwidget/searchresults",templateData);await Templates.replaceNodeContents(searchResultsContainer,html,js)};_exports.promisesAndResolvers=()=>{let bodyPromiseResolver;const bodyPromise=new Promise((resolve=>{bodyPromiseResolver=resolve}));return{bodyPromiseResolver:bodyPromiseResolver,bodyPromise:bodyPromise}}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.showLoader=_exports.registerListenerEvents=_exports.promisesAndResolvers=_exports.init=void 0,Templates=_interopRequireWildcard(Templates),Selectors=_interopRequireWildcard(Selectors),_notification=(obj=_notification)&&obj.__esModule?obj:{default:obj};_exports.init=async function(widgetContentContainer,bodyPromise,data,searchFunc){let unsearchableContent=arguments.length>4&&void 0!==arguments[4]?arguments[4]:null;bodyPromise.then((async bodyContent=>{if(widgetContentContainer.innerHTML=bodyContent,unsearchableContent){widgetContentContainer.querySelector(Selectors.regions.unsearchableContent).innerHTML+=unsearchableContent}const searchResultsContainer=widgetContentContainer.querySelector(Selectors.regions.searchResults);await showLoader(searchResultsContainer),await renderSearchResults(searchResultsContainer,data),registerListenerEvents(widgetContentContainer,data,searchFunc)})).catch(_notification.default.exception)};const registerListenerEvents=(widgetContentContainer,data,searchFunc)=>{const searchResultsContainer=widgetContentContainer.querySelector(Selectors.regions.searchResults),searchInput=widgetContentContainer.querySelector(Selectors.actions.search);searchInput.focus();const clearSearchButton=widgetContentContainer.querySelector(Selectors.actions.clearSearch);searchInput.addEventListener("input",(0,_utils.debounce)((async()=>{searchInput.value.length>0?clearSearchButton.classList.remove("d-none"):clearSearchButton.classList.add("d-none"),await renderSearchResults(searchResultsContainer,debounceCallee(searchInput.value,data,searchFunc()))}),300)),clearSearchButton.addEventListener("click",(async e=>{e.stopPropagation(),searchInput.value="",searchInput.focus(),clearSearchButton.classList.add("d-none"),await renderSearchResults(searchResultsContainer,debounceCallee(searchInput.value,data,searchFunc()))}))};_exports.registerListenerEvents=registerListenerEvents;const showLoader=async container=>{const{html:html,js:js}=await Templates.renderForPromise("core_grades/searchwidget/loading",{});Templates.replaceNodeContents(container,html,js)};_exports.showLoader=showLoader;const debounceCallee=(searchValue,data,searchFunction)=>searchValue.length>0?searchFunction(data,searchValue):data,renderSearchResults=async(searchResultsContainer,searchResultsData)=>{const templateData={searchresults:searchResultsData},{html:html,js:js}=await Templates.renderForPromise("core_grades/searchwidget/searchresults",templateData);await Templates.replaceNodeContents(searchResultsContainer,html,js)};_exports.promisesAndResolvers=()=>{let bodyPromiseResolver;const bodyPromise=new Promise((resolve=>{bodyPromiseResolver=resolve}));return{bodyPromiseResolver:bodyPromiseResolver,bodyPromise:bodyPromise}}})); //# sourceMappingURL=basewidget.min.js.map \ No newline at end of file diff --git a/grade/amd/build/searchwidget/basewidget.min.js.map b/grade/amd/build/searchwidget/basewidget.min.js.map index 60cc764b89e..4d136840e37 100644 --- a/grade/amd/build/searchwidget/basewidget.min.js.map +++ b/grade/amd/build/searchwidget/basewidget.min.js.map @@ -1 +1 @@ -{"version":3,"file":"basewidget.min.js","sources":["../../src/searchwidget/basewidget.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 * A small modal to search users or grade items within the gradebook.\n *\n * @module core_grades/searchwidget/basewidget\n * @copyright 2022 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport * as ModalFactory from 'core/modal_factory';\nimport * as ModalEvents from 'core/modal_events';\nimport {debounce} from 'core/utils';\nimport * as Templates from 'core/templates';\nimport {comboBox} from 'core/aria';\n\n/**\n * Build the base searching widget.\n *\n * @method init\n * @param {Promise} bodyPromise The promise from the callee of the contents to place in the modal body.\n * @param {Array} data An array of all the data generated by the callee.\n * @param {Function} searchFunc Partially applied function we need to manage search the passed dataset.\n * @param {string} modalTitle The name of the search widget.\n * @param {string|null} unsearchableContent The content rendered in a non-searchable area.\n */\nexport const init = (bodyPromise, data, searchFunc, modalTitle, unsearchableContent = null) => {\n const modal = buildModal(bodyPromise, modalTitle);\n registerListenerEvents(modal, data, searchFunc, unsearchableContent);\n};\n\n/**\n * Register chooser related event listeners.\n *\n * @method registerListenerEvents\n * @param {Promise} modal Our modal that we are working with.\n * @param {Array} data An array of all the data generated by the callee.\n * @param {Function} searchFunc Partially applied function we need to manage search the passed dataset.\n * @param {string|null} unsearchableContent The content rendered in a non-searchable area.\n */\nconst registerListenerEvents = (modal, data, searchFunc, unsearchableContent) => {\n modal.then(modal => {\n // We want to destroy this when the dialog is closed.\n modal.getRoot().on(ModalEvents.hidden, () => {\n modal.destroy();\n });\n // Once the body of the modal has been resolved, add more features.\n modal.getBodyPromise()\n // The return value of getBodyPromise is a jquery object containing the body NodeElement.\n .then(body => body[0])\n .then(body => {\n const searchInput = body.querySelector('input[data-action=\"search\"]');\n const searchResultsContainer = body.querySelector('[data-region=\"search-results-container-widget\"]');\n\n renderSearchResults(searchResultsContainer, data);\n\n if (unsearchableContent) {\n const unsearchableContentContainer = body.querySelector(\n '[data-region=\"unsearchable-content-container-widget\"]');\n unsearchableContentContainer.innerHTML += unsearchableContent;\n }\n\n // The search input is triggered.\n searchInput.addEventListener('input', debounce(async() => {\n // Display the search results.\n await renderSearchResults(\n searchResultsContainer,\n debounceCallee(\n searchInput.value,\n data,\n searchFunc()\n )\n );\n }, 300));\n // Trigger event handling for the results in line with aria guidelines.\n comboBox(searchInput);\n return body;\n }).catch();\n }).catch();\n};\n\n/**\n * Given an object we want to build a modal ready to show.\n *\n * @method buildModal\n * @param {Promise} bodyPromise Body promise that the caller should resolve.\n * @param {string} modalTitle The name of the search widget.\n * @return {Object} The modal ready to display immediately and render body in later.\n */\nconst buildModal = (bodyPromise, modalTitle) => {\n return ModalFactory.create({\n type: ModalFactory.types.DEFAULT,\n title: modalTitle,\n body: bodyPromise,\n small: true,\n scrollable: false,\n templateContext: {\n classes: 'reportdatasearch modal-sm'\n }\n }).then(modal => {\n modal.show();\n return modal;\n });\n};\n\n/**\n * We have a small helper that'll call the curried search function allowing callers to filter\n * the data set however we want rather than defining how data must be filtered.\n *\n * @method debounceCallee\n * @param {String} searchValue The input from the user that we'll search against.\n * @param {Array} data An array of all the data generated by the callee.\n * @param {Function} searchFunction Partially applied function we need to manage search the passed dataset.\n * @return {Array} The filtered subset of the provided data that we'll then render into the results.\n */\nconst debounceCallee = (searchValue, data, searchFunction) => {\n if (searchValue.length > 0) { // Search query is present.\n return searchFunction(data, searchValue);\n }\n return data;\n};\n\n/**\n * Given the output of the callers' search function, render out the results into the modal.\n *\n * @method renderSearchResults\n * @param {HTMLElement} searchResultsContainer The DOM node of the widget where we'll render the provided results.\n * @param {Array} searchResultsData The filtered subset of the provided data that we'll then render into the results.\n */\nconst renderSearchResults = async(searchResultsContainer, searchResultsData) => {\n const templateData = {\n 'searchresults': searchResultsData,\n };\n // Build up the html & js ready to place into the help section.\n const {html, js} = await Templates.renderForPromise('core_grades/searchwidget/searchresults', templateData);\n await Templates.replaceNodeContents(searchResultsContainer, html, js);\n};\n\n/**\n * We want to create the basic promises and hooks that the caller will implement, so we can build modals\n * ahead of time and allow the caller to resolve their promises once complete.\n *\n * @method promisesAndResolvers\n * @returns {{bodyPromise: Promise, bodyPromiseResolver}}\n */\nexport const promisesAndResolvers = () => {\n // We want to show the modal instantly but loading whilst waiting for our data.\n let bodyPromiseResolver;\n const bodyPromise = new Promise(resolve => {\n bodyPromiseResolver = resolve;\n });\n\n return {bodyPromiseResolver, bodyPromise};\n};\n"],"names":["bodyPromise","data","searchFunc","modalTitle","unsearchableContent","modal","buildModal","registerListenerEvents","then","getRoot","on","ModalEvents","hidden","destroy","getBodyPromise","body","searchInput","querySelector","searchResultsContainer","renderSearchResults","innerHTML","addEventListener","async","debounceCallee","value","catch","ModalFactory","create","type","types","DEFAULT","title","small","scrollable","templateContext","classes","show","searchValue","searchFunction","length","searchResultsData","templateData","html","js","Templates","renderForPromise","replaceNodeContents","bodyPromiseResolver","Promise","resolve"],"mappings":";;;;;;;+QAsCoB,SAACA,YAAaC,KAAMC,WAAYC,gBAAYC,2EAAsB,WAC5EC,MAAQC,WAAWN,YAAaG,YACtCI,uBAAuBF,MAAOJ,KAAMC,WAAYE,4BAY9CG,uBAAyB,CAACF,MAAOJ,KAAMC,WAAYE,uBACrDC,MAAMG,MAAKH,QAEPA,MAAMI,UAAUC,GAAGC,YAAYC,QAAQ,KACnCP,MAAMQ,aAGVR,MAAMS,iBAEDN,MAAKO,MAAQA,KAAK,KAClBP,MAAKO,aACIC,YAAcD,KAAKE,cAAc,+BACjCC,uBAAyBH,KAAKE,cAAc,sDAElDE,oBAAoBD,uBAAwBjB,MAExCG,oBAAqB,CACgBW,KAAKE,cACtC,yDACyBG,WAAahB,2BAI9CY,YAAYK,iBAAiB,SAAS,oBAASC,gBAErCH,oBACFD,uBACAK,eACIP,YAAYQ,MACZvB,KACAC,iBAGT,yBAEMc,aACFD,QACRU,WACRA,SAWDnB,WAAa,CAACN,YAAaG,aACtBuB,aAAaC,OAAO,CACvBC,KAAMF,aAAaG,MAAMC,QACzBC,MAAO5B,WACPY,KAAMf,YACNgC,OAAO,EACPC,YAAY,EACZC,gBAAiB,CACbC,QAAS,+BAEd3B,MAAKH,QACJA,MAAM+B,OACC/B,SAcTkB,eAAiB,CAACc,YAAapC,KAAMqC,iBACnCD,YAAYE,OAAS,EACdD,eAAerC,KAAMoC,aAEzBpC,KAULkB,oBAAsBG,MAAMJ,uBAAwBsB,2BAChDC,aAAe,eACAD,oBAGfE,KAACA,KAADC,GAAOA,UAAYC,UAAUC,iBAAiB,yCAA0CJ,oBACxFG,UAAUE,oBAAoB5B,uBAAwBwB,KAAMC,mCAUlC,SAE5BI,0BACE/C,YAAc,IAAIgD,SAAQC,UAC5BF,oBAAsBE,iBAGnB,CAACF,oBAAAA,oBAAqB/C,YAAAA"} \ No newline at end of file +{"version":3,"file":"basewidget.min.js","sources":["../../src/searchwidget/basewidget.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 * A widget to search users or grade items within the gradebook.\n *\n * @module core_grades/searchwidget/basewidget\n * @copyright 2022 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport {debounce} from 'core/utils';\nimport * as Templates from 'core/templates';\nimport * as Selectors from 'core_grades/searchwidget/selectors';\nimport Notification from 'core/notification';\n\n/**\n * Build the base searching widget.\n *\n * @method init\n * @param {HTMLElement} widgetContentContainer The selector for the widget container element.\n * @param {Promise} bodyPromise The promise from the callee of the contents to place in the widget container.\n * @param {Array} data An array of all the data generated by the callee.\n * @param {Function} searchFunc Partially applied function we need to manage search the passed dataset.\n * @param {string|null} unsearchableContent The content rendered in a non-searchable area.\n */\nexport const init = async(widgetContentContainer, bodyPromise, data, searchFunc, unsearchableContent = null) => {\n bodyPromise.then(async(bodyContent) => {\n // Render the body content.\n widgetContentContainer.innerHTML = bodyContent;\n\n // Render the unsearchable content if defined.\n if (unsearchableContent) {\n const unsearchableContentContainer = widgetContentContainer.querySelector(Selectors.regions.unsearchableContent);\n unsearchableContentContainer.innerHTML += unsearchableContent;\n }\n\n const searchResultsContainer = widgetContentContainer.querySelector(Selectors.regions.searchResults);\n // Display a loader until the search results are rendered.\n await showLoader(searchResultsContainer);\n // Render the search results.\n await renderSearchResults(searchResultsContainer, data);\n\n registerListenerEvents(widgetContentContainer, data, searchFunc);\n\n }).catch(Notification.exception);\n};\n\n/**\n * Register the event listeners for the search widget.\n *\n * @method registerListenerEvents\n * @param {HTMLElement} widgetContentContainer The selector for the widget container element.\n * @param {Array} data An array of all the data generated by the callee.\n * @param {Function} searchFunc Partially applied function we need to manage search the passed dataset.\n */\nexport const registerListenerEvents = (widgetContentContainer, data, searchFunc) => {\n const searchResultsContainer = widgetContentContainer.querySelector(Selectors.regions.searchResults);\n const searchInput = widgetContentContainer.querySelector(Selectors.actions.search);\n // We want to focus on the first known user interable element within the dropdown.\n searchInput.focus();\n const clearSearchButton = widgetContentContainer.querySelector(Selectors.actions.clearSearch);\n\n // The search input is triggered.\n searchInput.addEventListener('input', debounce(async() => {\n // If search query is present display the 'clear search' button, otherwise hide it.\n if (searchInput.value.length > 0) {\n clearSearchButton.classList.remove('d-none');\n } else {\n clearSearchButton.classList.add('d-none');\n }\n // Display the search results.\n await renderSearchResults(\n searchResultsContainer,\n debounceCallee(\n searchInput.value,\n data,\n searchFunc()\n )\n );\n }, 300));\n\n // Clear search is triggered.\n clearSearchButton.addEventListener('click', async(e) => {\n e.stopPropagation();\n // Clear the entered search query in the search bar.\n searchInput.value = \"\";\n searchInput.focus();\n clearSearchButton.classList.add('d-none');\n\n // Display all results.\n await renderSearchResults(\n searchResultsContainer,\n debounceCallee(\n searchInput.value,\n data,\n searchFunc()\n )\n );\n });\n};\n\n/**\n * Renders the loading placeholder for the search widget.\n *\n * @method showLoader\n * @param {HTMLElement} container The DOM node where we'll render the loading placeholder.\n */\nexport const showLoader = async(container) => {\n const {html, js} = await Templates.renderForPromise('core_grades/searchwidget/loading', {});\n Templates.replaceNodeContents(container, html, js);\n};\n\n/**\n * We have a small helper that'll call the curried search function allowing callers to filter\n * the data set however we want rather than defining how data must be filtered.\n *\n * @method debounceCallee\n * @param {String} searchValue The input from the user that we'll search against.\n * @param {Array} data An array of all the data generated by the callee.\n * @param {Function} searchFunction Partially applied function we need to manage search the passed dataset.\n * @return {Array} The filtered subset of the provided data that we'll then render into the results.\n */\nconst debounceCallee = (searchValue, data, searchFunction) => {\n if (searchValue.length > 0) { // Search query is present.\n return searchFunction(data, searchValue);\n }\n return data;\n};\n\n/**\n * Given the output of the callers' search function, render out the results into the search results container.\n *\n * @method renderSearchResults\n * @param {HTMLElement} searchResultsContainer The DOM node of the widget where we'll render the provided results.\n * @param {Array} searchResultsData The filtered subset of the provided data that we'll then render into the results.\n */\nconst renderSearchResults = async(searchResultsContainer, searchResultsData) => {\n const templateData = {\n 'searchresults': searchResultsData,\n };\n // Build up the html & js ready to place into the help section.\n const {html, js} = await Templates.renderForPromise('core_grades/searchwidget/searchresults', templateData);\n await Templates.replaceNodeContents(searchResultsContainer, html, js);\n};\n\n/**\n * We want to create the basic promises and hooks that the caller will implement, so we can build the search widget\n * ahead of time and allow the caller to resolve their promises once complete.\n *\n * @method promisesAndResolvers\n * @returns {{bodyPromise: Promise, bodyPromiseResolver}}\n */\nexport const promisesAndResolvers = () => {\n // We want to show the widget instantly but loading whilst waiting for our data.\n let bodyPromiseResolver;\n const bodyPromise = new Promise(resolve => {\n bodyPromiseResolver = resolve;\n });\n\n return {bodyPromiseResolver, bodyPromise};\n};\n"],"names":["async","widgetContentContainer","bodyPromise","data","searchFunc","unsearchableContent","then","innerHTML","bodyContent","querySelector","Selectors","regions","searchResultsContainer","searchResults","showLoader","renderSearchResults","registerListenerEvents","catch","Notification","exception","searchInput","actions","search","focus","clearSearchButton","clearSearch","addEventListener","value","length","classList","remove","add","debounceCallee","e","stopPropagation","html","js","Templates","renderForPromise","replaceNodeContents","container","searchValue","searchFunction","searchResultsData","templateData","bodyPromiseResolver","Promise","resolve"],"mappings":";;;;;;;gVAqCoBA,eAAMC,uBAAwBC,YAAaC,KAAMC,gBAAYC,2EAAsB,KACnGH,YAAYI,MAAKN,MAAAA,iBAEbC,uBAAuBM,UAAYC,YAG/BH,oBAAqB,CACgBJ,uBAAuBQ,cAAcC,UAAUC,QAAQN,qBAC/DE,WAAaF,0BAGxCO,uBAAyBX,uBAAuBQ,cAAcC,UAAUC,QAAQE,qBAEhFC,WAAWF,8BAEXG,oBAAoBH,uBAAwBT,MAElDa,uBAAuBf,uBAAwBE,KAAMC,eAEtDa,MAAMC,sBAAaC,kBAWbH,uBAAyB,CAACf,uBAAwBE,KAAMC,oBAC3DQ,uBAAyBX,uBAAuBQ,cAAcC,UAAUC,QAAQE,eAChFO,YAAcnB,uBAAuBQ,cAAcC,UAAUW,QAAQC,QAE3EF,YAAYG,cACNC,kBAAoBvB,uBAAuBQ,cAAcC,UAAUW,QAAQI,aAGjFL,YAAYM,iBAAiB,SAAS,oBAAS1B,UAEvCoB,YAAYO,MAAMC,OAAS,EAC3BJ,kBAAkBK,UAAUC,OAAO,UAEnCN,kBAAkBK,UAAUE,IAAI,gBAG9BhB,oBACFH,uBACAoB,eACIZ,YAAYO,MACZxB,KACAC,iBAGT,MAGHoB,kBAAkBE,iBAAiB,SAAS1B,MAAAA,IACxCiC,EAAEC,kBAEFd,YAAYO,MAAQ,GACpBP,YAAYG,QACZC,kBAAkBK,UAAUE,IAAI,gBAG1BhB,oBACFH,uBACAoB,eACIZ,YAAYO,MACZxB,KACAC,gFAYHU,WAAad,MAAAA,kBAChBmC,KAACA,KAADC,GAAOA,UAAYC,UAAUC,iBAAiB,mCAAoC,IACxFD,UAAUE,oBAAoBC,UAAWL,KAAMC,0CAa7CJ,eAAiB,CAACS,YAAatC,KAAMuC,iBACnCD,YAAYb,OAAS,EACdc,eAAevC,KAAMsC,aAEzBtC,KAULY,oBAAsBf,MAAMY,uBAAwB+B,2BAChDC,aAAe,eACAD,oBAGfR,KAACA,KAADC,GAAOA,UAAYC,UAAUC,iBAAiB,yCAA0CM,oBACxFP,UAAUE,oBAAoB3B,uBAAwBuB,KAAMC,mCAUlC,SAE5BS,0BACE3C,YAAc,IAAI4C,SAAQC,UAC5BF,oBAAsBE,iBAGnB,CAACF,oBAAAA,oBAAqB3C,YAAAA"} \ No newline at end of file diff --git a/grade/amd/build/searchwidget/group.min.js b/grade/amd/build/searchwidget/group.min.js index 5ab94e45af9..9bac0576b75 100644 --- a/grade/amd/build/searchwidget/group.min.js +++ b/grade/amd/build/searchwidget/group.min.js @@ -1,10 +1,10 @@ -define("core_grades/searchwidget/group",["exports","core/pending","core/templates","core/custom_interaction_events","core_grades/searchwidget/repository","core_grades/searchwidget/basewidget","core/str"],(function(_exports,_pending,Templates,_custom_interaction_events,Repository,WidgetBase,_str){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} +define("core_grades/searchwidget/group",["exports","core/pending","core/templates","core_grades/searchwidget/repository","core_grades/searchwidget/basewidget","jquery","core_grades/searchwidget/selectors"],(function(_exports,_pending,Templates,Repository,WidgetBase,_jquery,Selectors){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} /** - * A small modal to search groups within the gradebook. + * A widget to search groups within the gradebook. * * @module core_grades/searchwidget/group * @copyright 2022 Mathew May * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_pending=_interopRequireDefault(_pending),Templates=_interopRequireWildcard(Templates),_custom_interaction_events=_interopRequireDefault(_custom_interaction_events),Repository=_interopRequireWildcard(Repository),WidgetBase=_interopRequireWildcard(WidgetBase);let registered=!1;_exports.init=()=>{if(registered)return;const pendingPromise=new _pending.default;registerListenerEvents(),pendingPromise.resolve(),registered=!0};const registerListenerEvents=()=>{const events=["click",_custom_interaction_events.default.events.activate,_custom_interaction_events.default.events.keyboardActivate];_custom_interaction_events.default.define(document,events);let{bodyPromiseResolver:bodyPromiseResolver,bodyPromise:bodyPromise}=WidgetBase.promisesAndResolvers();events.forEach((event=>{document.addEventListener(event,(async e=>{const trigger=e.target.closest(".groupwidget");if(trigger){const courseID=trigger.dataset.courseid,actionBaseUrl=trigger.dataset.actionBaseUrl;e.preventDefault();const data=await Repository.groupFetch(courseID,actionBaseUrl).catch((async e=>{const errorTemplateData={errormessage:e.message};bodyPromiseResolver(await Templates.render("core_grades/searchwidget/error",errorTemplateData))}));if(data===[])return;WidgetBase.init(bodyPromise,data.groups,searchGroups(),(0,_str.get_string)("selectagroup","core"))}}))})),bodyPromiseResolver(Templates.render("core_grades/searchwidget/group/groupsearch_body",[]))},searchGroups=()=>()=>(groups,searchTerm)=>{if(""===searchTerm)return groups;searchTerm=searchTerm.toLowerCase();const searchResults=[];return groups.forEach((group=>{group.name.toLowerCase().includes(searchTerm)&&searchResults.push(group)})),searchResults}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_pending=_interopRequireDefault(_pending),Templates=_interopRequireWildcard(Templates),Repository=_interopRequireWildcard(Repository),WidgetBase=_interopRequireWildcard(WidgetBase),_jquery=_interopRequireDefault(_jquery),Selectors=_interopRequireWildcard(Selectors);let registered=!1;_exports.init=()=>{if(registered)return;const pendingPromise=new _pending.default;registerListenerEvents(),pendingPromise.resolve(),registered=!0};const registerListenerEvents=()=>{let{bodyPromiseResolver:bodyPromiseResolver,bodyPromise:bodyPromise}=WidgetBase.promisesAndResolvers();const dropdownMenuContainer=document.querySelector(Selectors.elements.getSearchWidgetDropdownSelector("group"));(0,_jquery.default)(Selectors.elements.getSearchWidgetSelector("group")).on("show.bs.dropdown",(async e=>{const courseID=e.relatedTarget.dataset.courseid,actionBaseUrl=e.relatedTarget.dataset.actionBaseUrl;await WidgetBase.showLoader(dropdownMenuContainer);const data=await Repository.groupFetch(courseID,actionBaseUrl).catch((async e=>{const errorTemplateData={errormessage:e.message};bodyPromiseResolver(await Templates.render("core_grades/searchwidget/error",errorTemplateData))}));data!==[]&&await WidgetBase.init(dropdownMenuContainer,bodyPromise,data.groups,searchGroups())})),bodyPromiseResolver(Templates.render("core_grades/searchwidget/group/groupsearch_body",[])),(0,_jquery.default)(Selectors.elements.getSearchWidgetSelector("group")).on("hide.bs.dropdown",(()=>{dropdownMenuContainer.innerHTML=""}))},searchGroups=()=>()=>(groups,searchTerm)=>{if(""===searchTerm)return groups;searchTerm=searchTerm.toLowerCase();const searchResults=[];return groups.forEach((group=>{group.name.toLowerCase().includes(searchTerm)&&searchResults.push(group)})),searchResults}})); //# sourceMappingURL=group.min.js.map \ No newline at end of file diff --git a/grade/amd/build/searchwidget/group.min.js.map b/grade/amd/build/searchwidget/group.min.js.map index 6ec32f69fe5..5d5973c9fe1 100644 --- a/grade/amd/build/searchwidget/group.min.js.map +++ b/grade/amd/build/searchwidget/group.min.js.map @@ -1 +1 @@ -{"version":3,"file":"group.min.js","sources":["../../src/searchwidget/group.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 * A small modal to search groups within the gradebook.\n *\n * @module core_grades/searchwidget/group\n * @copyright 2022 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Pending from 'core/pending';\nimport * as Templates from 'core/templates';\nimport CustomEvents from \"core/custom_interaction_events\";\nimport * as Repository from 'core_grades/searchwidget/repository';\nimport * as WidgetBase from 'core_grades/searchwidget/basewidget';\nimport {get_string as getString} from 'core/str';\n\n/**\n * Whether the event listener has already been registered for this module.\n *\n * @type {boolean}\n */\nlet registered = false;\n\n/**\n * Our entry point into starting to build the group search widget.\n *\n * It'll eventually, based upon the listeners, open the search widget and allow filtering.\n *\n * @method init\n */\nexport const init = () => {\n if (registered) {\n return;\n }\n const pendingPromise = new Pending();\n registerListenerEvents();\n pendingPromise.resolve();\n registered = true;\n};\n\n/**\n * Register event listeners.\n *\n * @method registerListenerEvents\n */\nconst registerListenerEvents = () => {\n const events = [\n 'click',\n CustomEvents.events.activate,\n CustomEvents.events.keyboardActivate\n ];\n CustomEvents.define(document, events);\n\n let {bodyPromiseResolver, bodyPromise} = WidgetBase.promisesAndResolvers();\n\n // Register events.\n events.forEach((event) => {\n document.addEventListener(event, async(e) => {\n const trigger = e.target.closest('.groupwidget');\n if (trigger) {\n const courseID = trigger.dataset.courseid;\n const actionBaseUrl = trigger.dataset.actionBaseUrl;\n e.preventDefault();\n\n // If an error occurs while fetching the data, display the error within the modal.\n const data = await Repository.groupFetch(courseID, actionBaseUrl).catch(async(e) => {\n const errorTemplateData = {\n 'errormessage': e.message\n };\n bodyPromiseResolver(\n await Templates.render('core_grades/searchwidget/error', errorTemplateData)\n );\n });\n // Early return if there is no module data.\n if (data === []) {\n return;\n }\n WidgetBase.init(\n bodyPromise,\n data.groups,\n searchGroups(),\n getString('selectagroup', 'core')\n );\n }\n });\n });\n // Resolvers for passed functions in the modal creation.\n bodyPromiseResolver(Templates.render(\n 'core_grades/searchwidget/group/groupsearch_body',\n []\n ));\n};\n\n/**\n * Define how we want to search and filter groups when the user decides to input a search value.\n *\n * @method registerListenerEvents\n * @returns {function(): function(*, *): (*)}\n */\nconst searchGroups = () => {\n return () => {\n return (groups, searchTerm) => {\n if (searchTerm === '') {\n return groups;\n }\n searchTerm = searchTerm.toLowerCase();\n const searchResults = [];\n groups.forEach((group) => {\n const groupName = group.name.toLowerCase();\n if (groupName.includes(searchTerm)) {\n searchResults.push(group);\n }\n });\n return searchResults;\n };\n };\n};\n"],"names":["registered","pendingPromise","Pending","registerListenerEvents","resolve","events","CustomEvents","activate","keyboardActivate","define","document","bodyPromiseResolver","bodyPromise","WidgetBase","promisesAndResolvers","forEach","event","addEventListener","async","trigger","e","target","closest","courseID","dataset","courseid","actionBaseUrl","preventDefault","data","Repository","groupFetch","catch","errorTemplateData","message","Templates","render","init","groups","searchGroups","searchTerm","toLowerCase","searchResults","group","name","includes","push"],"mappings":";;;;;;;yVAmCIA,YAAa,gBASG,QACZA,wBAGEC,eAAiB,IAAIC,iBAC3BC,yBACAF,eAAeG,UACfJ,YAAa,SAQXG,uBAAyB,WACrBE,OAAS,CACX,QACAC,mCAAaD,OAAOE,SACpBD,mCAAaD,OAAOG,qDAEXC,OAAOC,SAAUL,YAE1BM,oBAACA,oBAADC,YAAsBA,aAAeC,WAAWC,uBAGpDT,OAAOU,SAASC,QACZN,SAASO,iBAAiBD,OAAOE,MAAAA,UACvBC,QAAUC,EAAEC,OAAOC,QAAQ,mBAC7BH,QAAS,OACHI,SAAWJ,QAAQK,QAAQC,SAC3BC,cAAgBP,QAAQK,QAAQE,cACtCN,EAAEO,uBAGIC,WAAaC,WAAWC,WAAWP,SAAUG,eAAeK,OAAMb,MAAAA,UAC9Dc,kBAAoB,cACNZ,EAAEa,SAEtBtB,0BACUuB,UAAUC,OAAO,iCAAkCH,0BAI7DJ,OAAS,UAGbf,WAAWuB,KACPxB,YACAgB,KAAKS,OACLC,gBACA,mBAAU,eAAgB,gBAM1C3B,oBAAoBuB,UAAUC,OAC1B,kDACA,MAUFG,aAAe,IACV,IACI,CAACD,OAAQE,iBACO,KAAfA,kBACOF,OAEXE,WAAaA,WAAWC,oBAClBC,cAAgB,UACtBJ,OAAOtB,SAAS2B,QACMA,MAAMC,KAAKH,cACfI,SAASL,aACnBE,cAAcI,KAAKH,UAGpBD"} \ No newline at end of file +{"version":3,"file":"group.min.js","sources":["../../src/searchwidget/group.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 * A widget to search groups within the gradebook.\n *\n * @module core_grades/searchwidget/group\n * @copyright 2022 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Pending from 'core/pending';\nimport * as Templates from 'core/templates';\nimport * as Repository from 'core_grades/searchwidget/repository';\nimport * as WidgetBase from 'core_grades/searchwidget/basewidget';\nimport $ from 'jquery';\nimport * as Selectors from 'core_grades/searchwidget/selectors';\n\n/**\n * Whether the event listener has already been registered for this module.\n *\n * @type {boolean}\n */\nlet registered = false;\n\n/**\n * Our entry point into starting to build the group search widget.\n *\n * It'll eventually, based upon the listeners, open the search widget and allow filtering.\n *\n * @method init\n */\nexport const init = () => {\n if (registered) {\n return;\n }\n const pendingPromise = new Pending();\n registerListenerEvents();\n pendingPromise.resolve();\n registered = true;\n};\n\n/**\n * Register event listeners.\n *\n * @method registerListenerEvents\n */\nconst registerListenerEvents = () => {\n let {bodyPromiseResolver, bodyPromise} = WidgetBase.promisesAndResolvers();\n const dropdownMenuContainer = document.querySelector(Selectors.elements.getSearchWidgetDropdownSelector('group'));\n\n // Handle the 'shown.bs.dropdown' event (Fired when the dropdown menu is fully displayed).\n $(Selectors.elements.getSearchWidgetSelector('group')).on('show.bs.dropdown', async(e) => {\n const courseID = e.relatedTarget.dataset.courseid;\n const actionBaseUrl = e.relatedTarget.dataset.actionBaseUrl;\n // Display a loading icon in the dropdown menu container until the body promise is resolved.\n await WidgetBase.showLoader(dropdownMenuContainer);\n\n // If an error occurs while fetching the data, display the error within the dropdown menu.\n const data = await Repository.groupFetch(courseID, actionBaseUrl).catch(async(e) => {\n const errorTemplateData = {\n 'errormessage': e.message\n };\n bodyPromiseResolver(\n await Templates.render('core_grades/searchwidget/error', errorTemplateData)\n );\n });\n // Early return if there is no module data.\n if (data === []) {\n return;\n }\n await WidgetBase.init(\n dropdownMenuContainer,\n bodyPromise,\n data.groups,\n searchGroups(),\n );\n });\n\n // Resolvers for passed functions in the dropdown creation.\n bodyPromiseResolver(Templates.render(\n 'core_grades/searchwidget/group/groupsearch_body',\n []\n ));\n\n // Handle the 'hide.bs.dropdown' event (Fired when the dropdown menu is being closed).\n $(Selectors.elements.getSearchWidgetSelector('group')).on('hide.bs.dropdown', () => {\n // Reset the state once the groups menu dropdown is closed.\n dropdownMenuContainer.innerHTML = '';\n });\n};\n\n/**\n * Define how we want to search and filter groups when the user decides to input a search value.\n *\n * @method registerListenerEvents\n * @returns {function(): function(*, *): (*)}\n */\nconst searchGroups = () => {\n return () => {\n return (groups, searchTerm) => {\n if (searchTerm === '') {\n return groups;\n }\n searchTerm = searchTerm.toLowerCase();\n const searchResults = [];\n groups.forEach((group) => {\n const groupName = group.name.toLowerCase();\n if (groupName.includes(searchTerm)) {\n searchResults.push(group);\n }\n });\n return searchResults;\n };\n };\n};\n"],"names":["registered","pendingPromise","Pending","registerListenerEvents","resolve","bodyPromiseResolver","bodyPromise","WidgetBase","promisesAndResolvers","dropdownMenuContainer","document","querySelector","Selectors","elements","getSearchWidgetDropdownSelector","getSearchWidgetSelector","on","async","courseID","e","relatedTarget","dataset","courseid","actionBaseUrl","showLoader","data","Repository","groupFetch","catch","errorTemplateData","message","Templates","render","init","groups","searchGroups","innerHTML","searchTerm","toLowerCase","searchResults","forEach","group","name","includes","push"],"mappings":";;;;;;;gWAmCIA,YAAa,gBASG,QACZA,wBAGEC,eAAiB,IAAIC,iBAC3BC,yBACAF,eAAeG,UACfJ,YAAa,SAQXG,uBAAyB,SACvBE,oBAACA,oBAADC,YAAsBA,aAAeC,WAAWC,6BAC9CC,sBAAwBC,SAASC,cAAcC,UAAUC,SAASC,gCAAgC,8BAGtGF,UAAUC,SAASE,wBAAwB,UAAUC,GAAG,oBAAoBC,MAAAA,UACpEC,SAAWC,EAAEC,cAAcC,QAAQC,SACnCC,cAAgBJ,EAAEC,cAAcC,QAAQE,oBAExChB,WAAWiB,WAAWf,6BAGtBgB,WAAaC,WAAWC,WAAWT,SAAUK,eAAeK,OAAMX,MAAAA,UAC9DY,kBAAoB,cACNV,EAAEW,SAEtBzB,0BACU0B,UAAUC,OAAO,iCAAkCH,uBAI7DJ,OAAS,UAGPlB,WAAW0B,KACbxB,sBACAH,YACAmB,KAAKS,OACLC,mBAKR9B,oBAAoB0B,UAAUC,OAC1B,kDACA,yBAIFpB,UAAUC,SAASE,wBAAwB,UAAUC,GAAG,oBAAoB,KAE1EP,sBAAsB2B,UAAY,OAUpCD,aAAe,IACV,IACI,CAACD,OAAQG,iBACO,KAAfA,kBACOH,OAEXG,WAAaA,WAAWC,oBAClBC,cAAgB,UACtBL,OAAOM,SAASC,QACMA,MAAMC,KAAKJ,cACfK,SAASN,aACnBE,cAAcK,KAAKH,UAGpBF"} \ No newline at end of file diff --git a/grade/amd/build/searchwidget/selectors.min.js b/grade/amd/build/searchwidget/selectors.min.js new file mode 100644 index 00000000000..5a3cf5ceaa0 --- /dev/null +++ b/grade/amd/build/searchwidget/selectors.min.js @@ -0,0 +1,3 @@ +define("core_grades/searchwidget/selectors",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;return _exports.default={regions:{searchResults:'[data-region="search-results-container-widget"]',unsearchableContent:'[data-region="unsearchable-content-container-widget"]'},actions:{search:'[data-action="search"]',clearSearch:'[data-action="clearsearch"]'},elements:{getSearchWidgetSelector:searchtype=>'.search-widget[data-searchtype="'.concat(searchtype,'"]'),getSearchWidgetDropdownSelector:searchtype=>'.search-widget[data-searchtype="'.concat(searchtype,'"] .dropdown-menu')}},_exports.default})); + +//# sourceMappingURL=selectors.min.js.map \ No newline at end of file diff --git a/grade/amd/build/searchwidget/selectors.min.js.map b/grade/amd/build/searchwidget/selectors.min.js.map new file mode 100644 index 00000000000..6b1c4d7e8ed --- /dev/null +++ b/grade/amd/build/searchwidget/selectors.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"selectors.min.js","sources":["../../src/searchwidget/selectors.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 * Selectors for the search widget.\n *\n * @module core_grades/searchwidget/selectors\n * @copyright 2022 Mihail Geshoski \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nexport default {\n regions: {\n searchResults: '[data-region=\"search-results-container-widget\"]',\n unsearchableContent: '[data-region=\"unsearchable-content-container-widget\"]',\n },\n actions: {\n search: '[data-action=\"search\"]',\n clearSearch: '[data-action=\"clearsearch\"]',\n },\n elements: {\n getSearchWidgetSelector: searchtype => `.search-widget[data-searchtype=\"${searchtype}\"]`,\n getSearchWidgetDropdownSelector: searchtype => `.search-widget[data-searchtype=\"${searchtype}\"] .dropdown-menu`,\n },\n};\n"],"names":["regions","searchResults","unsearchableContent","actions","search","clearSearch","elements","getSearchWidgetSelector","searchtype","getSearchWidgetDropdownSelector"],"mappings":"oLAuBe,CACXA,QAAS,CACLC,cAAe,kDACfC,oBAAqB,yDAEzBC,QAAS,CACLC,OAAQ,yBACRC,YAAa,+BAEjBC,SAAU,CACNC,wBAAyBC,sDAAiDA,iBAC1EC,gCAAiCD,sDAAiDA"} \ No newline at end of file diff --git a/grade/amd/src/searchwidget/basewidget.js b/grade/amd/src/searchwidget/basewidget.js index d208cc7a490..6f66768e62a 100644 --- a/grade/amd/src/searchwidget/basewidget.js +++ b/grade/amd/src/searchwidget/basewidget.js @@ -14,105 +14,112 @@ // along with Moodle. If not, see . /** - * A small modal to search users or grade items within the gradebook. + * A widget to search users or grade items within the gradebook. * * @module core_grades/searchwidget/basewidget * @copyright 2022 Mathew May * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -import * as ModalFactory from 'core/modal_factory'; -import * as ModalEvents from 'core/modal_events'; import {debounce} from 'core/utils'; import * as Templates from 'core/templates'; -import {comboBox} from 'core/aria'; +import * as Selectors from 'core_grades/searchwidget/selectors'; +import Notification from 'core/notification'; /** * Build the base searching widget. * * @method init - * @param {Promise} bodyPromise The promise from the callee of the contents to place in the modal body. + * @param {HTMLElement} widgetContentContainer The selector for the widget container element. + * @param {Promise} bodyPromise The promise from the callee of the contents to place in the widget container. * @param {Array} data An array of all the data generated by the callee. * @param {Function} searchFunc Partially applied function we need to manage search the passed dataset. - * @param {string} modalTitle The name of the search widget. * @param {string|null} unsearchableContent The content rendered in a non-searchable area. */ -export const init = (bodyPromise, data, searchFunc, modalTitle, unsearchableContent = null) => { - const modal = buildModal(bodyPromise, modalTitle); - registerListenerEvents(modal, data, searchFunc, unsearchableContent); +export const init = async(widgetContentContainer, bodyPromise, data, searchFunc, unsearchableContent = null) => { + bodyPromise.then(async(bodyContent) => { + // Render the body content. + widgetContentContainer.innerHTML = bodyContent; + + // Render the unsearchable content if defined. + if (unsearchableContent) { + const unsearchableContentContainer = widgetContentContainer.querySelector(Selectors.regions.unsearchableContent); + unsearchableContentContainer.innerHTML += unsearchableContent; + } + + const searchResultsContainer = widgetContentContainer.querySelector(Selectors.regions.searchResults); + // Display a loader until the search results are rendered. + await showLoader(searchResultsContainer); + // Render the search results. + await renderSearchResults(searchResultsContainer, data); + + registerListenerEvents(widgetContentContainer, data, searchFunc); + + }).catch(Notification.exception); }; /** - * Register chooser related event listeners. + * Register the event listeners for the search widget. * * @method registerListenerEvents - * @param {Promise} modal Our modal that we are working with. + * @param {HTMLElement} widgetContentContainer The selector for the widget container element. * @param {Array} data An array of all the data generated by the callee. * @param {Function} searchFunc Partially applied function we need to manage search the passed dataset. - * @param {string|null} unsearchableContent The content rendered in a non-searchable area. */ -const registerListenerEvents = (modal, data, searchFunc, unsearchableContent) => { - modal.then(modal => { - // We want to destroy this when the dialog is closed. - modal.getRoot().on(ModalEvents.hidden, () => { - modal.destroy(); - }); - // Once the body of the modal has been resolved, add more features. - modal.getBodyPromise() - // The return value of getBodyPromise is a jquery object containing the body NodeElement. - .then(body => body[0]) - .then(body => { - const searchInput = body.querySelector('input[data-action="search"]'); - const searchResultsContainer = body.querySelector('[data-region="search-results-container-widget"]'); +export const registerListenerEvents = (widgetContentContainer, data, searchFunc) => { + const searchResultsContainer = widgetContentContainer.querySelector(Selectors.regions.searchResults); + const searchInput = widgetContentContainer.querySelector(Selectors.actions.search); + // We want to focus on the first known user interable element within the dropdown. + searchInput.focus(); + const clearSearchButton = widgetContentContainer.querySelector(Selectors.actions.clearSearch); - renderSearchResults(searchResultsContainer, data); + // The search input is triggered. + searchInput.addEventListener('input', debounce(async() => { + // If search query is present display the 'clear search' button, otherwise hide it. + if (searchInput.value.length > 0) { + clearSearchButton.classList.remove('d-none'); + } else { + clearSearchButton.classList.add('d-none'); + } + // Display the search results. + await renderSearchResults( + searchResultsContainer, + debounceCallee( + searchInput.value, + data, + searchFunc() + ) + ); + }, 300)); - if (unsearchableContent) { - const unsearchableContentContainer = body.querySelector( - '[data-region="unsearchable-content-container-widget"]'); - unsearchableContentContainer.innerHTML += unsearchableContent; - } + // Clear search is triggered. + clearSearchButton.addEventListener('click', async(e) => { + e.stopPropagation(); + // Clear the entered search query in the search bar. + searchInput.value = ""; + searchInput.focus(); + clearSearchButton.classList.add('d-none'); - // The search input is triggered. - searchInput.addEventListener('input', debounce(async() => { - // Display the search results. - await renderSearchResults( - searchResultsContainer, - debounceCallee( - searchInput.value, - data, - searchFunc() - ) - ); - }, 300)); - // Trigger event handling for the results in line with aria guidelines. - comboBox(searchInput); - return body; - }).catch(); - }).catch(); + // Display all results. + await renderSearchResults( + searchResultsContainer, + debounceCallee( + searchInput.value, + data, + searchFunc() + ) + ); + }); }; /** - * Given an object we want to build a modal ready to show. + * Renders the loading placeholder for the search widget. * - * @method buildModal - * @param {Promise} bodyPromise Body promise that the caller should resolve. - * @param {string} modalTitle The name of the search widget. - * @return {Object} The modal ready to display immediately and render body in later. + * @method showLoader + * @param {HTMLElement} container The DOM node where we'll render the loading placeholder. */ -const buildModal = (bodyPromise, modalTitle) => { - return ModalFactory.create({ - type: ModalFactory.types.DEFAULT, - title: modalTitle, - body: bodyPromise, - small: true, - scrollable: false, - templateContext: { - classes: 'reportdatasearch modal-sm' - } - }).then(modal => { - modal.show(); - return modal; - }); +export const showLoader = async(container) => { + const {html, js} = await Templates.renderForPromise('core_grades/searchwidget/loading', {}); + Templates.replaceNodeContents(container, html, js); }; /** @@ -133,7 +140,7 @@ const debounceCallee = (searchValue, data, searchFunction) => { }; /** - * Given the output of the callers' search function, render out the results into the modal. + * Given the output of the callers' search function, render out the results into the search results container. * * @method renderSearchResults * @param {HTMLElement} searchResultsContainer The DOM node of the widget where we'll render the provided results. @@ -149,14 +156,14 @@ const renderSearchResults = async(searchResultsContainer, searchResultsData) => }; /** - * We want to create the basic promises and hooks that the caller will implement, so we can build modals + * We want to create the basic promises and hooks that the caller will implement, so we can build the search widget * ahead of time and allow the caller to resolve their promises once complete. * * @method promisesAndResolvers * @returns {{bodyPromise: Promise, bodyPromiseResolver}} */ export const promisesAndResolvers = () => { - // We want to show the modal instantly but loading whilst waiting for our data. + // We want to show the widget instantly but loading whilst waiting for our data. let bodyPromiseResolver; const bodyPromise = new Promise(resolve => { bodyPromiseResolver = resolve; diff --git a/grade/amd/src/searchwidget/group.js b/grade/amd/src/searchwidget/group.js index e6170c33216..2882d6316c0 100644 --- a/grade/amd/src/searchwidget/group.js +++ b/grade/amd/src/searchwidget/group.js @@ -14,7 +14,7 @@ // along with Moodle. If not, see . /** - * A small modal to search groups within the gradebook. + * A widget to search groups within the gradebook. * * @module core_grades/searchwidget/group * @copyright 2022 Mathew May @@ -23,10 +23,10 @@ import Pending from 'core/pending'; import * as Templates from 'core/templates'; -import CustomEvents from "core/custom_interaction_events"; import * as Repository from 'core_grades/searchwidget/repository'; import * as WidgetBase from 'core_grades/searchwidget/basewidget'; -import {get_string as getString} from 'core/str'; +import $ from 'jquery'; +import * as Selectors from 'core_grades/searchwidget/selectors'; /** * Whether the event listener has already been registered for this module. @@ -58,51 +58,48 @@ export const init = () => { * @method registerListenerEvents */ const registerListenerEvents = () => { - const events = [ - 'click', - CustomEvents.events.activate, - CustomEvents.events.keyboardActivate - ]; - CustomEvents.define(document, events); - let {bodyPromiseResolver, bodyPromise} = WidgetBase.promisesAndResolvers(); + const dropdownMenuContainer = document.querySelector(Selectors.elements.getSearchWidgetDropdownSelector('group')); - // Register events. - events.forEach((event) => { - document.addEventListener(event, async(e) => { - const trigger = e.target.closest('.groupwidget'); - if (trigger) { - const courseID = trigger.dataset.courseid; - const actionBaseUrl = trigger.dataset.actionBaseUrl; - e.preventDefault(); + // Handle the 'shown.bs.dropdown' event (Fired when the dropdown menu is fully displayed). + $(Selectors.elements.getSearchWidgetSelector('group')).on('show.bs.dropdown', async(e) => { + const courseID = e.relatedTarget.dataset.courseid; + const actionBaseUrl = e.relatedTarget.dataset.actionBaseUrl; + // Display a loading icon in the dropdown menu container until the body promise is resolved. + await WidgetBase.showLoader(dropdownMenuContainer); - // If an error occurs while fetching the data, display the error within the modal. - const data = await Repository.groupFetch(courseID, actionBaseUrl).catch(async(e) => { - const errorTemplateData = { - 'errormessage': e.message - }; - bodyPromiseResolver( - await Templates.render('core_grades/searchwidget/error', errorTemplateData) - ); - }); - // Early return if there is no module data. - if (data === []) { - return; - } - WidgetBase.init( - bodyPromise, - data.groups, - searchGroups(), - getString('selectagroup', 'core') - ); - } + // If an error occurs while fetching the data, display the error within the dropdown menu. + const data = await Repository.groupFetch(courseID, actionBaseUrl).catch(async(e) => { + const errorTemplateData = { + 'errormessage': e.message + }; + bodyPromiseResolver( + await Templates.render('core_grades/searchwidget/error', errorTemplateData) + ); }); + // Early return if there is no module data. + if (data === []) { + return; + } + await WidgetBase.init( + dropdownMenuContainer, + bodyPromise, + data.groups, + searchGroups(), + ); }); - // Resolvers for passed functions in the modal creation. + + // Resolvers for passed functions in the dropdown creation. bodyPromiseResolver(Templates.render( 'core_grades/searchwidget/group/groupsearch_body', [] )); + + // Handle the 'hide.bs.dropdown' event (Fired when the dropdown menu is being closed). + $(Selectors.elements.getSearchWidgetSelector('group')).on('hide.bs.dropdown', () => { + // Reset the state once the groups menu dropdown is closed. + dropdownMenuContainer.innerHTML = ''; + }); }; /** diff --git a/grade/amd/src/searchwidget/selectors.js b/grade/amd/src/searchwidget/selectors.js new file mode 100644 index 00000000000..d283ec94736 --- /dev/null +++ b/grade/amd/src/searchwidget/selectors.js @@ -0,0 +1,37 @@ +// This file is part of Moodle - http://moodle.org/ +// +// Moodle is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Moodle is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Moodle. If not, see . + +/** + * Selectors for the search widget. + * + * @module core_grades/searchwidget/selectors + * @copyright 2022 Mihail Geshoski + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +export default { + regions: { + searchResults: '[data-region="search-results-container-widget"]', + unsearchableContent: '[data-region="unsearchable-content-container-widget"]', + }, + actions: { + search: '[data-action="search"]', + clearSearch: '[data-action="clearsearch"]', + }, + elements: { + getSearchWidgetSelector: searchtype => `.search-widget[data-searchtype="${searchtype}"]`, + getSearchWidgetDropdownSelector: searchtype => `.search-widget[data-searchtype="${searchtype}"] .dropdown-menu`, + }, +}; diff --git a/grade/classes/external/get_enrolled_users_for_search_widget.php b/grade/classes/external/get_enrolled_users_for_search_widget.php index 37bfc2b0a6f..91e16f6de88 100644 --- a/grade/classes/external/get_enrolled_users_for_search_widget.php +++ b/grade/classes/external/get_enrolled_users_for_search_widget.php @@ -105,6 +105,7 @@ class get_enrolled_users_for_search_widget extends external_api { $userpicture->size = 1; $user->profileimage = $userpicture->get_url($PAGE)->out(false); $user->email = $guiuser->email; + $user->active = false; // @TODO MDL-76246 $users[] = $user; } @@ -151,6 +152,7 @@ class get_enrolled_users_for_search_widget extends external_api { core_user::get_property_type('email'), 'An email address - allow email as root@localhost', VALUE_OPTIONAL), + 'active' => new external_value(PARAM_BOOL, 'Are we currently on this item?', VALUE_REQUIRED) ]; return new external_single_structure($userfields); } diff --git a/grade/classes/external/get_groups_for_search_widget.php b/grade/classes/external/get_groups_for_search_widget.php index 9e3d527c973..6424318a873 100644 --- a/grade/classes/external/get_groups_for_search_widget.php +++ b/grade/classes/external/get_groups_for_search_widget.php @@ -116,8 +116,10 @@ class get_groups_for_search_widget extends external_api { 'group' => $group->id ]); return (object) [ + 'id' => $group->id, 'name' => $group->name, 'url' => $url->out(false), + 'active' => false // @TODO MDL-76246 ]; }, $groupsmenu); } @@ -147,8 +149,10 @@ class get_groups_for_search_widget extends external_api { */ public static function group_description(): external_description { $groupfields = [ + 'id' => new external_value(PARAM_ALPHANUM, 'An ID for the group', VALUE_REQUIRED), 'url' => new external_value(PARAM_URL, 'The link that applies the group action', VALUE_REQUIRED), 'name' => new external_value(PARAM_TEXT, 'The full name of the group', VALUE_REQUIRED), + 'active' => new external_value(PARAM_BOOL, 'Are we currently on this item?', VALUE_REQUIRED) ]; return new external_single_structure($groupfields); } diff --git a/grade/report/singleview/amd/build/grade.min.js b/grade/report/singleview/amd/build/grade.min.js index 7f2ec52a68f..97635d1baa0 100644 --- a/grade/report/singleview/amd/build/grade.min.js +++ b/grade/report/singleview/amd/build/grade.min.js @@ -1,10 +1,10 @@ -define("gradereport_singleview/grade",["exports","core/pending","core/templates","core/custom_interaction_events","core_grades/searchwidget/repository","core_grades/searchwidget/basewidget","core/str"],(function(_exports,_pending,Templates,_custom_interaction_events,Repository,WidgetBase,_str){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} +define("gradereport_singleview/grade",["exports","core/pending","core/templates","core_grades/searchwidget/repository","core_grades/searchwidget/basewidget","jquery","core_grades/searchwidget/selectors"],(function(_exports,_pending,Templates,Repository,WidgetBase,_jquery,Selectors){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} /** - * A small modal to search users within the gradebook. + * A small modal to search grade items within the gradebook. * * @module gradereport_singleview * @copyright 2022 Mathew May * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_pending=_interopRequireDefault(_pending),Templates=_interopRequireWildcard(Templates),_custom_interaction_events=_interopRequireDefault(_custom_interaction_events),Repository=_interopRequireWildcard(Repository),WidgetBase=_interopRequireWildcard(WidgetBase);_exports.init=()=>{const pendingPromise=new _pending.default;registerListenerEvents(),pendingPromise.resolve()};const registerListenerEvents=()=>{const events=["click",_custom_interaction_events.default.events.activate,_custom_interaction_events.default.events.keyboardActivate];_custom_interaction_events.default.define(document,events);let{bodyPromiseResolver:bodyPromiseResolver,bodyPromise:bodyPromise}=WidgetBase.promisesAndResolvers();events.forEach((event=>{document.addEventListener(event,(async e=>{const trigger=e.target.closest(".gradewidget");if(trigger){const courseID=trigger.dataset.courseid;e.preventDefault();const data=await Repository.gradeitemFetch(courseID).catch((async e=>{const errorTemplateData={errormessage:e.message};bodyPromiseResolver(await Templates.render("core_grades/searchwidget/error",errorTemplateData))}));if(data===[]||void 0===data)return;WidgetBase.init(bodyPromise,data.gradeitems,searchGradeitems(),(0,_str.get_string)("selectagrade","gradereport_singleview"))}}))})),bodyPromiseResolver(Templates.render("gradereport_singleview/gradesearch_body",[]))},searchGradeitems=()=>()=>(modules,searchTerm)=>{if(""===searchTerm)return modules;searchTerm=searchTerm.toLowerCase();const searchResults=[];return modules.forEach((module=>{module.name.toLowerCase().includes(searchTerm)&&searchResults.push(module)})),searchResults}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_pending=_interopRequireDefault(_pending),Templates=_interopRequireWildcard(Templates),Repository=_interopRequireWildcard(Repository),WidgetBase=_interopRequireWildcard(WidgetBase),_jquery=_interopRequireDefault(_jquery),Selectors=_interopRequireWildcard(Selectors);_exports.init=()=>{const pendingPromise=new _pending.default;registerListenerEvents(),pendingPromise.resolve()};const registerListenerEvents=()=>{let{bodyPromiseResolver:bodyPromiseResolver,bodyPromise:bodyPromise}=WidgetBase.promisesAndResolvers();const dropdownMenuContainer=document.querySelector(Selectors.elements.getSearchWidgetDropdownSelector("grade"));(0,_jquery.default)(Selectors.elements.getSearchWidgetSelector("grade")).on("show.bs.dropdown",(async e=>{const courseID=e.relatedTarget.dataset.courseid;await WidgetBase.showLoader(dropdownMenuContainer);const data=await Repository.gradeitemFetch(courseID).catch((async e=>{const errorTemplateData={errormessage:e.message};bodyPromiseResolver(await Templates.render("core_grades/searchwidget/error",errorTemplateData))}));data!==[]&&(await WidgetBase.init(dropdownMenuContainer,bodyPromise,data.gradeitems,searchGradeitems()),bodyPromiseResolver(Templates.render("gradereport_singleview/gradesearch_body",[])))})),(0,_jquery.default)(Selectors.elements.getSearchWidgetSelector("grade")).on("hide.bs.dropdown",(()=>{dropdownMenuContainer.innerHTML=""}))},searchGradeitems=()=>()=>(modules,searchTerm)=>{if(""===searchTerm)return modules;searchTerm=searchTerm.toLowerCase();const searchResults=[];return modules.forEach((module=>{module.name.toLowerCase().includes(searchTerm)&&searchResults.push(module)})),searchResults}})); //# sourceMappingURL=grade.min.js.map \ No newline at end of file diff --git a/grade/report/singleview/amd/build/grade.min.js.map b/grade/report/singleview/amd/build/grade.min.js.map index 1ec797a12d0..ba46337c248 100644 --- a/grade/report/singleview/amd/build/grade.min.js.map +++ b/grade/report/singleview/amd/build/grade.min.js.map @@ -1 +1 @@ -{"version":3,"file":"grade.min.js","sources":["../src/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 .\n\n/**\n * A small modal to search users within the gradebook.\n *\n * @module gradereport_singleview\n * @copyright 2022 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Pending from 'core/pending';\nimport * as Templates from 'core/templates';\nimport CustomEvents from \"core/custom_interaction_events\";\nimport * as Repository from 'core_grades/searchwidget/repository';\nimport * as WidgetBase from 'core_grades/searchwidget/basewidget';\nimport {get_string as getString} from 'core/str';\n\n/**\n * Our entry point into starting to build the search widget.\n * It'll eventually, based upon the listeners, open the search widget and allow filtering.\n *\n * @method init\n */\nexport const init = () => {\n const pendingPromise = new Pending();\n registerListenerEvents();\n pendingPromise.resolve();\n};\n\n/**\n * Register grade item search widget related event listeners.\n *\n * @method registerListenerEvents\n */\nconst registerListenerEvents = () => {\n const events = [\n 'click',\n CustomEvents.events.activate,\n CustomEvents.events.keyboardActivate\n ];\n CustomEvents.define(document, events);\n\n let {bodyPromiseResolver, bodyPromise} = WidgetBase.promisesAndResolvers();\n\n // Register events.\n events.forEach((event) => {\n document.addEventListener(event, async(e) => {\n const trigger = e.target.closest('.gradewidget');\n if (trigger) {\n const courseID = trigger.dataset.courseid;\n e.preventDefault();\n\n // If an error occurs while fetching the data, display the error within the modal.\n const data = await Repository.gradeitemFetch(courseID).catch(async(e) => {\n const errorTemplateData = {\n 'errormessage': e.message\n };\n bodyPromiseResolver(\n await Templates.render('core_grades/searchwidget/error', errorTemplateData)\n );\n });\n // Early return if there is no module data.\n if (data === [] || data === undefined) {\n return;\n }\n WidgetBase.init(\n bodyPromise,\n data.gradeitems,\n searchGradeitems(),\n getString('selectagrade', 'gradereport_singleview')\n );\n }\n });\n });\n // Resolvers for passed functions in the modal creation.\n bodyPromiseResolver(Templates.render(\n 'gradereport_singleview/gradesearch_body',\n []\n ));\n};\n\n/**\n * Define how we want to search and filter grade items when the user decides to input a search value.\n *\n * @method registerListenerEvents\n * @returns {function(): function(*, *): (*)}\n */\nconst searchGradeitems = () => {\n return () => {\n return (modules, searchTerm) => {\n if (searchTerm === '') {\n return modules;\n }\n searchTerm = searchTerm.toLowerCase();\n const searchResults = [];\n modules.forEach((module) => {\n const moduleName = module.name.toLowerCase();\n if (moduleName.includes(searchTerm)) {\n searchResults.push(module);\n }\n });\n return searchResults;\n };\n };\n};\n"],"names":["pendingPromise","Pending","registerListenerEvents","resolve","events","CustomEvents","activate","keyboardActivate","define","document","bodyPromiseResolver","bodyPromise","WidgetBase","promisesAndResolvers","forEach","event","addEventListener","async","trigger","e","target","closest","courseID","dataset","courseid","preventDefault","data","Repository","gradeitemFetch","catch","errorTemplateData","message","Templates","render","undefined","init","gradeitems","searchGradeitems","modules","searchTerm","toLowerCase","searchResults","module","name","includes","push"],"mappings":";;;;;;;mWAoCoB,WACVA,eAAiB,IAAIC,iBAC3BC,yBACAF,eAAeG,iBAQbD,uBAAyB,WACrBE,OAAS,CACX,QACAC,mCAAaD,OAAOE,SACpBD,mCAAaD,OAAOG,qDAEXC,OAAOC,SAAUL,YAE1BM,oBAACA,oBAADC,YAAsBA,aAAeC,WAAWC,uBAGpDT,OAAOU,SAASC,QACZN,SAASO,iBAAiBD,OAAOE,MAAAA,UACvBC,QAAUC,EAAEC,OAAOC,QAAQ,mBAC7BH,QAAS,OACHI,SAAWJ,QAAQK,QAAQC,SACjCL,EAAEM,uBAGIC,WAAaC,WAAWC,eAAeN,UAAUO,OAAMZ,MAAAA,UACnDa,kBAAoB,cACNX,EAAEY,SAEtBrB,0BACUsB,UAAUC,OAAO,iCAAkCH,0BAI7DJ,OAAS,SAAeQ,IAATR,YAGnBd,WAAWuB,KACPxB,YACAe,KAAKU,WACLC,oBACA,mBAAU,eAAgB,kCAM1C3B,oBAAoBsB,UAAUC,OAC1B,0CACA,MAUFI,iBAAmB,IACd,IACI,CAACC,QAASC,iBACM,KAAfA,kBACOD,QAEXC,WAAaA,WAAWC,oBAClBC,cAAgB,UACtBH,QAAQxB,SAAS4B,SACMA,OAAOC,KAAKH,cAChBI,SAASL,aACpBE,cAAcI,KAAKH,WAGpBD"} \ No newline at end of file +{"version":3,"file":"grade.min.js","sources":["../src/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 .\n\n/**\n * A small modal to search grade items within the gradebook.\n *\n * @module gradereport_singleview\n * @copyright 2022 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Pending from 'core/pending';\nimport * as Templates from 'core/templates';\nimport * as Repository from 'core_grades/searchwidget/repository';\nimport * as WidgetBase from 'core_grades/searchwidget/basewidget';\nimport $ from 'jquery';\nimport * as Selectors from 'core_grades/searchwidget/selectors';\n\n/**\n * Our entry point into starting to build the search widget.\n * It'll eventually, based upon the listeners, open the search widget and allow filtering.\n *\n * @method init\n */\nexport const init = () => {\n const pendingPromise = new Pending();\n registerListenerEvents();\n pendingPromise.resolve();\n};\n\n/**\n * Register grade item search widget related event listeners.\n *\n * @method registerListenerEvents\n */\nconst registerListenerEvents = () => {\n let {bodyPromiseResolver, bodyPromise} = WidgetBase.promisesAndResolvers();\n const dropdownMenuContainer = document.querySelector(Selectors.elements.getSearchWidgetDropdownSelector('grade'));\n\n // Handle the 'shown.bs.dropdown' event (Fired when the dropdown menu is fully displayed).\n $(Selectors.elements.getSearchWidgetSelector('grade')).on('show.bs.dropdown', async(e) => {\n const courseID = e.relatedTarget.dataset.courseid;\n // Display a loading icon in the dropdown menu container until the body promise is resolved.\n await WidgetBase.showLoader(dropdownMenuContainer);\n\n // If an error occurs while fetching the data, display the error within the modal.\n const data = await Repository.gradeitemFetch(courseID).catch(async(e) => {\n const errorTemplateData = {\n 'errormessage': e.message\n };\n bodyPromiseResolver(\n await Templates.render('core_grades/searchwidget/error', errorTemplateData)\n );\n });\n\n // Early return if there is no module data.\n if (data === []) {\n return;\n }\n\n await WidgetBase.init(\n dropdownMenuContainer,\n bodyPromise,\n data.gradeitems,\n searchGradeitems()\n );\n\n // Resolvers for passed functions in the modal creation.\n bodyPromiseResolver(Templates.render('gradereport_singleview/gradesearch_body', []));\n });\n\n // Handle the 'hide.bs.dropdown' event (Fired when the dropdown menu is being closed).\n $(Selectors.elements.getSearchWidgetSelector('grade')).on('hide.bs.dropdown', () => {\n // Reset the state once the grade item menu dropdown is closed.\n dropdownMenuContainer.innerHTML = '';\n });\n};\n\n/**\n * Define how we want to search and filter grade items when the user decides to input a search value.\n *\n * @method registerListenerEvents\n * @returns {function(): function(*, *): (*)}\n */\nconst searchGradeitems = () => {\n return () => {\n return (modules, searchTerm) => {\n if (searchTerm === '') {\n return modules;\n }\n searchTerm = searchTerm.toLowerCase();\n const searchResults = [];\n modules.forEach((module) => {\n const moduleName = module.name.toLowerCase();\n if (moduleName.includes(searchTerm)) {\n searchResults.push(module);\n }\n });\n return searchResults;\n };\n };\n};\n"],"names":["pendingPromise","Pending","registerListenerEvents","resolve","bodyPromiseResolver","bodyPromise","WidgetBase","promisesAndResolvers","dropdownMenuContainer","document","querySelector","Selectors","elements","getSearchWidgetDropdownSelector","getSearchWidgetSelector","on","async","courseID","e","relatedTarget","dataset","courseid","showLoader","data","Repository","gradeitemFetch","catch","errorTemplateData","message","Templates","render","init","gradeitems","searchGradeitems","innerHTML","modules","searchTerm","toLowerCase","searchResults","forEach","module","name","includes","push"],"mappings":";;;;;;;0WAoCoB,WACVA,eAAiB,IAAIC,iBAC3BC,yBACAF,eAAeG,iBAQbD,uBAAyB,SACvBE,oBAACA,oBAADC,YAAsBA,aAAeC,WAAWC,6BAC9CC,sBAAwBC,SAASC,cAAcC,UAAUC,SAASC,gCAAgC,8BAGtGF,UAAUC,SAASE,wBAAwB,UAAUC,GAAG,oBAAoBC,MAAAA,UACpEC,SAAWC,EAAEC,cAAcC,QAAQC,eAEnCf,WAAWgB,WAAWd,6BAGtBe,WAAaC,WAAWC,eAAeR,UAAUS,OAAMV,MAAAA,UACnDW,kBAAoB,cACNT,EAAEU,SAEtBxB,0BACUyB,UAAUC,OAAO,iCAAkCH,uBAK7DJ,OAAS,WAIPjB,WAAWyB,KACbvB,sBACAH,YACAkB,KAAKS,WACLC,oBAIJ7B,oBAAoByB,UAAUC,OAAO,0CAA2C,6BAIlFnB,UAAUC,SAASE,wBAAwB,UAAUC,GAAG,oBAAoB,KAE1EP,sBAAsB0B,UAAY,OAUpCD,iBAAmB,IACd,IACI,CAACE,QAASC,iBACM,KAAfA,kBACOD,QAEXC,WAAaA,WAAWC,oBAClBC,cAAgB,UACtBH,QAAQI,SAASC,SACMA,OAAOC,KAAKJ,cAChBK,SAASN,aACpBE,cAAcK,KAAKH,WAGpBF"} \ No newline at end of file diff --git a/grade/report/singleview/amd/build/user.min.js b/grade/report/singleview/amd/build/user.min.js index 452a78e89e6..053c89338ae 100644 --- a/grade/report/singleview/amd/build/user.min.js +++ b/grade/report/singleview/amd/build/user.min.js @@ -1,10 +1,10 @@ -define("gradereport_singleview/user",["exports","core/pending","core/templates","core/custom_interaction_events","core_grades/searchwidget/repository","core_grades/searchwidget/basewidget","core/str","core/url"],(function(_exports,_pending,Templates,_custom_interaction_events,Repository,WidgetBase,_str,_url){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} +define("gradereport_singleview/user",["exports","core/pending","core/templates","core_grades/searchwidget/repository","core_grades/searchwidget/basewidget","core/url","jquery","core_grades/searchwidget/selectors"],(function(_exports,_pending,Templates,Repository,WidgetBase,_url,_jquery,Selectors){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} /** * A small modal to search users within the gradebook. * * @module gradereport_singleview/user * @copyright 2022 Mathew May * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_pending=_interopRequireDefault(_pending),Templates=_interopRequireWildcard(Templates),_custom_interaction_events=_interopRequireDefault(_custom_interaction_events),Repository=_interopRequireWildcard(Repository),WidgetBase=_interopRequireWildcard(WidgetBase),_url=_interopRequireDefault(_url);_exports.init=()=>{const pendingPromise=new _pending.default;registerListenerEvents(),pendingPromise.resolve()};const registerListenerEvents=()=>{const events=["click",_custom_interaction_events.default.events.activate,_custom_interaction_events.default.events.keyboardActivate];_custom_interaction_events.default.define(document,events);let{bodyPromiseResolver:bodyPromiseResolver,bodyPromise:bodyPromise}=WidgetBase.promisesAndResolvers();events.forEach((event=>{document.addEventListener(event,(async e=>{const trigger=e.target.closest(".userwidget");if(trigger){const courseID=trigger.dataset.courseid,groupId=trigger.dataset.groupid;e.preventDefault();const actionBaseUrl=_url.default.relativeUrl("/grade/report/singleview/index.php",{item:"user"},!1),data=await Repository.userFetch(courseID,actionBaseUrl,groupId).catch((async e=>{const errorTemplateData={errormessage:e.message};bodyPromiseResolver(await Templates.render("core_grades/searchwidget/error",errorTemplateData))}));if(data===[])return;WidgetBase.init(bodyPromise,data.users,searchUsers(),(0,_str.get_string)("selectauser","grades"))}}))})),bodyPromiseResolver(Templates.render("core_grades/searchwidget/user/usersearch_body",[]))},searchUsers=()=>()=>(users,searchTerm)=>{if(""===searchTerm)return users;searchTerm=searchTerm.toLowerCase();const searchResults=[];return users.forEach((user=>{user.fullname.toLowerCase().includes(searchTerm)&&searchResults.push(user)})),searchResults}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_pending=_interopRequireDefault(_pending),Templates=_interopRequireWildcard(Templates),Repository=_interopRequireWildcard(Repository),WidgetBase=_interopRequireWildcard(WidgetBase),_url=_interopRequireDefault(_url),_jquery=_interopRequireDefault(_jquery),Selectors=_interopRequireWildcard(Selectors);_exports.init=()=>{const pendingPromise=new _pending.default;registerListenerEvents(),pendingPromise.resolve()};const registerListenerEvents=()=>{let{bodyPromiseResolver:bodyPromiseResolver,bodyPromise:bodyPromise}=WidgetBase.promisesAndResolvers();const dropdownMenuContainer=document.querySelector(Selectors.elements.getSearchWidgetDropdownSelector("user"));(0,_jquery.default)(Selectors.elements.getSearchWidgetSelector("user")).on("show.bs.dropdown",(async e=>{const courseID=e.relatedTarget.dataset.courseid,groupId=e.relatedTarget.dataset.groupid,actionBaseUrl=_url.default.relativeUrl("/grade/report/singleview/index.php",{item:"user"},!1);await WidgetBase.showLoader(dropdownMenuContainer);const data=await Repository.userFetch(courseID,actionBaseUrl,groupId).catch((async e=>{const errorTemplateData={errormessage:e.message};bodyPromiseResolver(await Templates.render("core_grades/searchwidget/error",errorTemplateData))}));data!==[]&&(await WidgetBase.init(dropdownMenuContainer,bodyPromise,data.users,searchUsers()),bodyPromiseResolver(Templates.render("core_grades/searchwidget/user/usersearch_body",[])))})),(0,_jquery.default)(Selectors.elements.getSearchWidgetSelector("user")).on("hide.bs.dropdown",(()=>{dropdownMenuContainer.innerHTML=""}))},searchUsers=()=>()=>(users,searchTerm)=>{if(""===searchTerm)return users;searchTerm=searchTerm.toLowerCase();const searchResults=[];return users.forEach((user=>{user.fullname.toLowerCase().includes(searchTerm)&&searchResults.push(user)})),searchResults}})); //# sourceMappingURL=user.min.js.map \ No newline at end of file diff --git a/grade/report/singleview/amd/build/user.min.js.map b/grade/report/singleview/amd/build/user.min.js.map index 512f2687ed4..b164448fb99 100644 --- a/grade/report/singleview/amd/build/user.min.js.map +++ b/grade/report/singleview/amd/build/user.min.js.map @@ -1 +1 @@ -{"version":3,"file":"user.min.js","sources":["../src/user.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 * A small modal to search users within the gradebook.\n *\n * @module gradereport_singleview/user\n * @copyright 2022 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Pending from 'core/pending';\nimport * as Templates from 'core/templates';\nimport CustomEvents from \"core/custom_interaction_events\";\nimport * as Repository from 'core_grades/searchwidget/repository';\nimport * as WidgetBase from 'core_grades/searchwidget/basewidget';\nimport {get_string as getString} from 'core/str';\nimport Url from 'core/url';\n\n/**\n * Our entry point into starting to build the search widget.\n * It'll eventually, based upon the listeners, open the search widget and allow filtering.\n *\n * @method init\n */\nexport const init = () => {\n const pendingPromise = new Pending();\n registerListenerEvents();\n pendingPromise.resolve();\n};\n\n/**\n * Register user search widget related event listeners.\n *\n * @method registerListenerEvents\n */\nconst registerListenerEvents = () => {\n const events = [\n 'click',\n CustomEvents.events.activate,\n CustomEvents.events.keyboardActivate\n ];\n CustomEvents.define(document, events);\n\n let {bodyPromiseResolver, bodyPromise} = WidgetBase.promisesAndResolvers();\n\n // Register events.\n events.forEach((event) => {\n document.addEventListener(event, async(e) => {\n const trigger = e.target.closest('.userwidget');\n if (trigger) {\n const courseID = trigger.dataset.courseid;\n const groupId = trigger.dataset.groupid;\n e.preventDefault();\n\n const actionBaseUrl = Url.relativeUrl('/grade/report/singleview/index.php', {item: 'user'}, false);\n // If an error occurs while fetching the data, display the error within the modal.\n const data = await Repository.userFetch(courseID, actionBaseUrl, groupId).catch(async(e) => {\n const errorTemplateData = {\n 'errormessage': e.message\n };\n bodyPromiseResolver(\n await Templates.render('core_grades/searchwidget/error', errorTemplateData)\n );\n });\n // Early return if there is no module data.\n if (data === []) {\n return;\n }\n WidgetBase.init(\n bodyPromise,\n data.users,\n searchUsers(),\n getString('selectauser', 'grades')\n );\n }\n });\n });\n // Resolvers for passed functions in the modal creation.\n bodyPromiseResolver(Templates.render(\n 'core_grades/searchwidget/user/usersearch_body',\n []\n ));\n};\n\n/**\n * Define how we want to search and filter users when the user decides to input a search value.\n *\n * @method registerListenerEvents\n * @returns {function(): function(*, *): (*)}\n */\nconst searchUsers = () => {\n return () => {\n return (users, searchTerm) => {\n if (searchTerm === '') {\n return users;\n }\n searchTerm = searchTerm.toLowerCase();\n const searchResults = [];\n users.forEach((user) => {\n const userName = user.fullname.toLowerCase();\n if (userName.includes(searchTerm)) {\n searchResults.push(user);\n }\n });\n return searchResults;\n };\n };\n};\n"],"names":["pendingPromise","Pending","registerListenerEvents","resolve","events","CustomEvents","activate","keyboardActivate","define","document","bodyPromiseResolver","bodyPromise","WidgetBase","promisesAndResolvers","forEach","event","addEventListener","async","trigger","e","target","closest","courseID","dataset","courseid","groupId","groupid","preventDefault","actionBaseUrl","Url","relativeUrl","item","data","Repository","userFetch","catch","errorTemplateData","message","Templates","render","init","users","searchUsers","searchTerm","toLowerCase","searchResults","user","fullname","includes","push"],"mappings":";;;;;;;qYAqCoB,WACVA,eAAiB,IAAIC,iBAC3BC,yBACAF,eAAeG,iBAQbD,uBAAyB,WACrBE,OAAS,CACX,QACAC,mCAAaD,OAAOE,SACpBD,mCAAaD,OAAOG,qDAEXC,OAAOC,SAAUL,YAE1BM,oBAACA,oBAADC,YAAsBA,aAAeC,WAAWC,uBAGpDT,OAAOU,SAASC,QACZN,SAASO,iBAAiBD,OAAOE,MAAAA,UACvBC,QAAUC,EAAEC,OAAOC,QAAQ,kBAC7BH,QAAS,OACHI,SAAWJ,QAAQK,QAAQC,SAC3BC,QAAUP,QAAQK,QAAQG,QAChCP,EAAEQ,uBAEIC,cAAgBC,aAAIC,YAAY,qCAAsC,CAACC,KAAM,SAAS,GAEtFC,WAAaC,WAAWC,UAAUZ,SAAUM,cAAeH,SAASU,OAAMlB,MAAAA,UACtEmB,kBAAoB,cACNjB,EAAEkB,SAEtB3B,0BACU4B,UAAUC,OAAO,iCAAkCH,0BAI7DJ,OAAS,UAGbpB,WAAW4B,KACP7B,YACAqB,KAAKS,MACLC,eACA,mBAAU,cAAe,kBAMzChC,oBAAoB4B,UAAUC,OAC1B,gDACA,MAUFG,YAAc,IACT,IACI,CAACD,MAAOE,iBACQ,KAAfA,kBACOF,MAEXE,WAAaA,WAAWC,oBAClBC,cAAgB,UACtBJ,MAAM3B,SAASgC,OACMA,KAAKC,SAASH,cAClBI,SAASL,aAClBE,cAAcI,KAAKH,SAGpBD"} \ No newline at end of file +{"version":3,"file":"user.min.js","sources":["../src/user.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 * A small modal to search users within the gradebook.\n *\n * @module gradereport_singleview/user\n * @copyright 2022 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Pending from 'core/pending';\nimport * as Templates from 'core/templates';\nimport * as Repository from 'core_grades/searchwidget/repository';\nimport * as WidgetBase from 'core_grades/searchwidget/basewidget';\nimport Url from 'core/url';\nimport $ from 'jquery';\nimport * as Selectors from 'core_grades/searchwidget/selectors';\n\n/**\n * Our entry point into starting to build the search widget.\n * It'll eventually, based upon the listeners, open the search widget and allow filtering.\n *\n * @method init\n */\nexport const init = () => {\n const pendingPromise = new Pending();\n registerListenerEvents();\n pendingPromise.resolve();\n};\n\n/**\n * Register user search widget related event listeners.\n *\n * @method registerListenerEvents\n */\nconst registerListenerEvents = () => {\n let {bodyPromiseResolver, bodyPromise} = WidgetBase.promisesAndResolvers();\n const dropdownMenuContainer = document.querySelector(Selectors.elements.getSearchWidgetDropdownSelector('user'));\n\n // Handle the 'shown.bs.dropdown' event (Fired when the dropdown menu is fully displayed).\n $(Selectors.elements.getSearchWidgetSelector('user')).on('show.bs.dropdown', async(e) => {\n const courseID = e.relatedTarget.dataset.courseid;\n const groupId = e.relatedTarget.dataset.groupid;\n const actionBaseUrl = Url.relativeUrl('/grade/report/singleview/index.php', {item: 'user'}, false);\n // Display a loading icon in the dropdown menu container until the body promise is resolved.\n await WidgetBase.showLoader(dropdownMenuContainer);\n\n // If an error occurs while fetching the data, display the error within the dropdown menu.\n const data = await Repository.userFetch(courseID, actionBaseUrl, groupId).catch(async(e) => {\n const errorTemplateData = {\n 'errormessage': e.message\n };\n bodyPromiseResolver(\n await Templates.render('core_grades/searchwidget/error', errorTemplateData)\n );\n });\n\n // Early return if there is no module data.\n if (data === []) {\n return;\n }\n\n await WidgetBase.init(\n dropdownMenuContainer,\n bodyPromise,\n data.users,\n searchUsers()\n );\n\n // Resolvers for passed functions in the dropdown menu creation.\n bodyPromiseResolver(Templates.render('core_grades/searchwidget/user/usersearch_body', []));\n });\n\n // Handle the 'hide.bs.dropdown' event (Fired when the dropdown menu is being closed).\n $(Selectors.elements.getSearchWidgetSelector('user')).on('hide.bs.dropdown', () => {\n // Reset the state once the user menu dropdown is closed.\n dropdownMenuContainer.innerHTML = '';\n });\n};\n\n/**\n * Define how we want to search and filter users when the user decides to input a search value.\n *\n * @method registerListenerEvents\n * @returns {function(): function(*, *): (*)}\n */\nconst searchUsers = () => {\n return () => {\n return (users, searchTerm) => {\n if (searchTerm === '') {\n return users;\n }\n searchTerm = searchTerm.toLowerCase();\n const searchResults = [];\n users.forEach((user) => {\n const userName = user.fullname.toLowerCase();\n if (userName.includes(searchTerm)) {\n searchResults.push(user);\n }\n });\n return searchResults;\n };\n };\n};\n"],"names":["pendingPromise","Pending","registerListenerEvents","resolve","bodyPromiseResolver","bodyPromise","WidgetBase","promisesAndResolvers","dropdownMenuContainer","document","querySelector","Selectors","elements","getSearchWidgetDropdownSelector","getSearchWidgetSelector","on","async","courseID","e","relatedTarget","dataset","courseid","groupId","groupid","actionBaseUrl","Url","relativeUrl","item","showLoader","data","Repository","userFetch","catch","errorTemplateData","message","Templates","render","init","users","searchUsers","innerHTML","searchTerm","toLowerCase","searchResults","forEach","user","fullname","includes","push"],"mappings":";;;;;;;4YAqCoB,WACVA,eAAiB,IAAIC,iBAC3BC,yBACAF,eAAeG,iBAQbD,uBAAyB,SACvBE,oBAACA,oBAADC,YAAsBA,aAAeC,WAAWC,6BAC9CC,sBAAwBC,SAASC,cAAcC,UAAUC,SAASC,gCAAgC,6BAGtGF,UAAUC,SAASE,wBAAwB,SAASC,GAAG,oBAAoBC,MAAAA,UACnEC,SAAWC,EAAEC,cAAcC,QAAQC,SACnCC,QAAUJ,EAAEC,cAAcC,QAAQG,QAClCC,cAAgBC,aAAIC,YAAY,qCAAsC,CAACC,KAAM,SAAS,SAEtFrB,WAAWsB,WAAWpB,6BAGtBqB,WAAaC,WAAWC,UAAUd,SAAUO,cAAeF,SAASU,OAAMhB,MAAAA,UACtEiB,kBAAoB,cACNf,EAAEgB,SAEtB9B,0BACU+B,UAAUC,OAAO,iCAAkCH,uBAK7DJ,OAAS,WAIPvB,WAAW+B,KACb7B,sBACAH,YACAwB,KAAKS,MACLC,eAIJnC,oBAAoB+B,UAAUC,OAAO,gDAAiD,6BAIxFzB,UAAUC,SAASE,wBAAwB,SAASC,GAAG,oBAAoB,KAEzEP,sBAAsBgC,UAAY,OAUpCD,YAAc,IACT,IACI,CAACD,MAAOG,iBACQ,KAAfA,kBACOH,MAEXG,WAAaA,WAAWC,oBAClBC,cAAgB,UACtBL,MAAMM,SAASC,OACMA,KAAKC,SAASJ,cAClBK,SAASN,aAClBE,cAAcK,KAAKH,SAGpBF"} \ No newline at end of file diff --git a/grade/report/singleview/amd/src/grade.js b/grade/report/singleview/amd/src/grade.js index fca697e914c..c03ad94f844 100644 --- a/grade/report/singleview/amd/src/grade.js +++ b/grade/report/singleview/amd/src/grade.js @@ -14,7 +14,7 @@ // along with Moodle. If not, see . /** - * A small modal to search users within the gradebook. + * A small modal to search grade items within the gradebook. * * @module gradereport_singleview * @copyright 2022 Mathew May @@ -23,10 +23,10 @@ import Pending from 'core/pending'; import * as Templates from 'core/templates'; -import CustomEvents from "core/custom_interaction_events"; import * as Repository from 'core_grades/searchwidget/repository'; import * as WidgetBase from 'core_grades/searchwidget/basewidget'; -import {get_string as getString} from 'core/str'; +import $ from 'jquery'; +import * as Selectors from 'core_grades/searchwidget/selectors'; /** * Our entry point into starting to build the search widget. @@ -46,50 +46,46 @@ export const init = () => { * @method registerListenerEvents */ const registerListenerEvents = () => { - const events = [ - 'click', - CustomEvents.events.activate, - CustomEvents.events.keyboardActivate - ]; - CustomEvents.define(document, events); - let {bodyPromiseResolver, bodyPromise} = WidgetBase.promisesAndResolvers(); + const dropdownMenuContainer = document.querySelector(Selectors.elements.getSearchWidgetDropdownSelector('grade')); - // Register events. - events.forEach((event) => { - document.addEventListener(event, async(e) => { - const trigger = e.target.closest('.gradewidget'); - if (trigger) { - const courseID = trigger.dataset.courseid; - e.preventDefault(); + // Handle the 'shown.bs.dropdown' event (Fired when the dropdown menu is fully displayed). + $(Selectors.elements.getSearchWidgetSelector('grade')).on('show.bs.dropdown', async(e) => { + const courseID = e.relatedTarget.dataset.courseid; + // Display a loading icon in the dropdown menu container until the body promise is resolved. + await WidgetBase.showLoader(dropdownMenuContainer); - // If an error occurs while fetching the data, display the error within the modal. - const data = await Repository.gradeitemFetch(courseID).catch(async(e) => { - const errorTemplateData = { - 'errormessage': e.message - }; - bodyPromiseResolver( - await Templates.render('core_grades/searchwidget/error', errorTemplateData) - ); - }); - // Early return if there is no module data. - if (data === [] || data === undefined) { - return; - } - WidgetBase.init( - bodyPromise, - data.gradeitems, - searchGradeitems(), - getString('selectagrade', 'gradereport_singleview') - ); - } + // If an error occurs while fetching the data, display the error within the modal. + const data = await Repository.gradeitemFetch(courseID).catch(async(e) => { + const errorTemplateData = { + 'errormessage': e.message + }; + bodyPromiseResolver( + await Templates.render('core_grades/searchwidget/error', errorTemplateData) + ); }); + + // Early return if there is no module data. + if (data === []) { + return; + } + + await WidgetBase.init( + dropdownMenuContainer, + bodyPromise, + data.gradeitems, + searchGradeitems() + ); + + // Resolvers for passed functions in the modal creation. + bodyPromiseResolver(Templates.render('gradereport_singleview/gradesearch_body', [])); + }); + + // Handle the 'hide.bs.dropdown' event (Fired when the dropdown menu is being closed). + $(Selectors.elements.getSearchWidgetSelector('grade')).on('hide.bs.dropdown', () => { + // Reset the state once the grade item menu dropdown is closed. + dropdownMenuContainer.innerHTML = ''; }); - // Resolvers for passed functions in the modal creation. - bodyPromiseResolver(Templates.render( - 'gradereport_singleview/gradesearch_body', - [] - )); }; /** diff --git a/grade/report/singleview/amd/src/user.js b/grade/report/singleview/amd/src/user.js index 267ba0423e2..bbd16a8f845 100644 --- a/grade/report/singleview/amd/src/user.js +++ b/grade/report/singleview/amd/src/user.js @@ -23,11 +23,11 @@ import Pending from 'core/pending'; import * as Templates from 'core/templates'; -import CustomEvents from "core/custom_interaction_events"; import * as Repository from 'core_grades/searchwidget/repository'; import * as WidgetBase from 'core_grades/searchwidget/basewidget'; -import {get_string as getString} from 'core/str'; import Url from 'core/url'; +import $ from 'jquery'; +import * as Selectors from 'core_grades/searchwidget/selectors'; /** * Our entry point into starting to build the search widget. @@ -47,52 +47,48 @@ export const init = () => { * @method registerListenerEvents */ const registerListenerEvents = () => { - const events = [ - 'click', - CustomEvents.events.activate, - CustomEvents.events.keyboardActivate - ]; - CustomEvents.define(document, events); - let {bodyPromiseResolver, bodyPromise} = WidgetBase.promisesAndResolvers(); + const dropdownMenuContainer = document.querySelector(Selectors.elements.getSearchWidgetDropdownSelector('user')); - // Register events. - events.forEach((event) => { - document.addEventListener(event, async(e) => { - const trigger = e.target.closest('.userwidget'); - if (trigger) { - const courseID = trigger.dataset.courseid; - const groupId = trigger.dataset.groupid; - e.preventDefault(); + // Handle the 'shown.bs.dropdown' event (Fired when the dropdown menu is fully displayed). + $(Selectors.elements.getSearchWidgetSelector('user')).on('show.bs.dropdown', async(e) => { + const courseID = e.relatedTarget.dataset.courseid; + const groupId = e.relatedTarget.dataset.groupid; + const actionBaseUrl = Url.relativeUrl('/grade/report/singleview/index.php', {item: 'user'}, false); + // Display a loading icon in the dropdown menu container until the body promise is resolved. + await WidgetBase.showLoader(dropdownMenuContainer); - const actionBaseUrl = Url.relativeUrl('/grade/report/singleview/index.php', {item: 'user'}, false); - // If an error occurs while fetching the data, display the error within the modal. - const data = await Repository.userFetch(courseID, actionBaseUrl, groupId).catch(async(e) => { - const errorTemplateData = { - 'errormessage': e.message - }; - bodyPromiseResolver( - await Templates.render('core_grades/searchwidget/error', errorTemplateData) - ); - }); - // Early return if there is no module data. - if (data === []) { - return; - } - WidgetBase.init( - bodyPromise, - data.users, - searchUsers(), - getString('selectauser', 'grades') - ); - } + // If an error occurs while fetching the data, display the error within the dropdown menu. + const data = await Repository.userFetch(courseID, actionBaseUrl, groupId).catch(async(e) => { + const errorTemplateData = { + 'errormessage': e.message + }; + bodyPromiseResolver( + await Templates.render('core_grades/searchwidget/error', errorTemplateData) + ); }); + + // Early return if there is no module data. + if (data === []) { + return; + } + + await WidgetBase.init( + dropdownMenuContainer, + bodyPromise, + data.users, + searchUsers() + ); + + // Resolvers for passed functions in the dropdown menu creation. + bodyPromiseResolver(Templates.render('core_grades/searchwidget/user/usersearch_body', [])); + }); + + // Handle the 'hide.bs.dropdown' event (Fired when the dropdown menu is being closed). + $(Selectors.elements.getSearchWidgetSelector('user')).on('hide.bs.dropdown', () => { + // Reset the state once the user menu dropdown is closed. + dropdownMenuContainer.innerHTML = ''; }); - // Resolvers for passed functions in the modal creation. - bodyPromiseResolver(Templates.render( - 'core_grades/searchwidget/user/usersearch_body', - [] - )); }; /** diff --git a/grade/report/singleview/classes/external/singleview.php b/grade/report/singleview/classes/external/singleview.php index 6f73bbbe57d..6a1dd25472b 100644 --- a/grade/report/singleview/classes/external/singleview.php +++ b/grade/report/singleview/classes/external/singleview.php @@ -85,7 +85,7 @@ class singleview extends core_course_external { $gradeitems = array_map(function ($gradeitem) use ($PAGE, $USER, $params) { $item = new \stdClass(); - $item->gid = $gradeitem->id; + $item->id = $gradeitem->id; $url = new moodle_url('/grade/report/singleview/index.php', [ 'id' => $params['courseid'], 'itemid' => $gradeitem->id, @@ -94,6 +94,7 @@ class singleview extends core_course_external { ); $item->name = $gradeitem->get_name(true); $item->url = $url->out(false); + $item->active = false; // @TODO MDL-76246 return $item; }, $gradeableitems); @@ -114,13 +115,14 @@ class singleview extends core_course_external { return new external_single_structure([ 'gradeitems' => new external_multiple_structure( new external_single_structure([ - 'gid' => new external_value(PARAM_INT, 'ID of the grade item', VALUE_OPTIONAL), + 'id' => new external_value(PARAM_INT, 'ID of the grade item', VALUE_OPTIONAL), 'url' => new external_value( PARAM_URL, 'The link to the grade report', VALUE_OPTIONAL ), 'name' => new external_value(PARAM_TEXT, 'The full name of the grade item', VALUE_OPTIONAL), + 'active' => new external_value(PARAM_BOOL, 'Are we currently on this item?', VALUE_REQUIRED) ]) ), 'warnings' => new external_warnings(), diff --git a/grade/report/singleview/renderer.php b/grade/report/singleview/renderer.php index 70b7998e1cc..8663e2b7106 100644 --- a/grade/report/singleview/renderer.php +++ b/grade/report/singleview/renderer.php @@ -61,7 +61,7 @@ class gradereport_singleview_renderer extends plugin_renderer_base { } $this->page->requires->js_call_amd('gradereport_singleview/user', 'init'); - return $this->render_from_template('gradereport_singleview/user_selector', $data); + return $this->render_from_template('core_grades/user_selector', $data); } /** diff --git a/grade/report/singleview/styles.css b/grade/report/singleview/styles.css index 2fe1cc15f8a..9d02f09f702 100644 --- a/grade/report/singleview/styles.css +++ b/grade/report/singleview/styles.css @@ -147,7 +147,7 @@ overflow: auto; } -.path-grade-report-singleview .user-selector .userinitials { +.path-grade-report-singleview .search-widget[data-searchtype="user"] .userinitials { width: 40px; height: 40px; } diff --git a/grade/report/singleview/templates/grade_item_selector.mustache b/grade/report/singleview/templates/grade_item_selector.mustache index 890711a02ae..0f68a908c94 100644 --- a/grade/report/singleview/templates/grade_item_selector.mustache +++ b/grade/report/singleview/templates/grade_item_selector.mustache @@ -29,8 +29,8 @@ } } }} -
-