From a7ceae66dc5c3175a53a5da10c5a4e7fe34aef2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikel=20Mart=C3=ADn?= Date: Tue, 22 Jul 2025 16:46:38 +0200 Subject: [PATCH] MDL-85593 theme_boost: Update modchooser styles Update activity chooser UI: - Update related templates - Update related styles in core.scss - Fix related JS modules affected by the changes - Clean and improve existing styles --- .../local/activitychooser/dialogue.min.js | 2 +- .../local/activitychooser/dialogue.min.js.map | 2 +- .../local/activitychooser/dialoguedom.min.js | 2 +- .../activitychooser/dialoguedom.min.js.map | 2 +- .../local/activitychooser/exporter.min.js | 2 +- .../local/activitychooser/exporter.min.js.map | 2 +- .../amd/src/local/activitychooser/dialogue.js | 3 +- .../src/local/activitychooser/dialoguedom.js | 5 +- .../amd/src/local/activitychooser/exporter.js | 5 +- .../course/templates/activitychooser.mustache | 6 +- .../local/activitychooser/favourites.mustache | 2 +- .../local/activitychooser/help.mustache | 25 +- .../local/activitychooser/item.mustache | 49 ++- .../modchoosercontainer.mustache | 6 +- .../activitychooser/search_results.mustache | 2 +- .../local/activitychooser/tabcontent.mustache | 4 +- .../tests/behat/activity_chooser.feature | 4 - public/course/tests/behat/behat_course.php | 2 +- public/lang/en/course.php | 2 +- public/theme/boost/scss/moodle/core.scss | 321 +++++++++++------- public/theme/boost/style/moodle.css | 245 +++++++------ public/theme/classic/style/moodle.css | 245 +++++++------ 22 files changed, 561 insertions(+), 377 deletions(-) diff --git a/public/course/amd/build/local/activitychooser/dialogue.min.js b/public/course/amd/build/local/activitychooser/dialogue.min.js index f376394592d..4f21914ba29 100644 --- a/public/course/amd/build/local/activitychooser/dialogue.min.js +++ b/public/course/amd/build/local/activitychooser/dialogue.min.js @@ -1,3 +1,3 @@ -define("core_course/local/activitychooser/dialogue",["exports","core/utils","core_course/local/activitychooser/dialoguedom","core/key_codes","core_course/local/activitychooser/exporter","core/normalise","core/str","core/modal","core/modal_events","core/notification","core_course/local/activitychooser/repository","core_course/local/activitychooser/selectors","core/templates"],(function(_exports,_utils,_dialoguedom,_key_codes,_exporter,_normalise,_str,_modal,ModalEvents,_notification,Repository,_selectors,Templates){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.displayActivityChooserModal=async function(footerDataPromise,modulesDataPromise){let bodyPromiseResolver;const bodyPromise=new Promise((resolve=>{bodyPromiseResolver=resolve})),exporter=new _exporter.default,footerData=await footerDataPromise,footerPromise=Templates.render("core_course/local/activitychooser/footer",exporter.getFooterData(footerData)),sectionModal=_modal.default.create({body:bodyPromise,title:(0,_str.getString)("addresourceoractivity"),footer:footerPromise,large:!0,scrollable:!1,templateContext:{classes:"modchooser"},show:!0});try{const modulesData=await modulesDataPromise;if(!modulesData)return;const modal=await sectionModal;new ActivityChooserDialogue(modal,modulesData,footerData);const templateData=await exporter.getModChooserTemplateData(modulesData);bodyPromiseResolver(await Templates.render("core_course/activitychooser",templateData))}catch(error){const errorTemplateData={errormessage:error.message};return void bodyPromiseResolver(await Templates.render("core_course/local/activitychooser/error",errorTemplateData))}},_exports.displayChooser=void 0,_dialoguedom=_interopRequireDefault(_dialoguedom),_exporter=_interopRequireDefault(_exporter),_modal=_interopRequireDefault(_modal),ModalEvents=_interopRequireWildcard(ModalEvents),_notification=_interopRequireDefault(_notification),Repository=_interopRequireWildcard(Repository),_selectors=_interopRequireDefault(_selectors),Templates=_interopRequireWildcard(Templates);var _systemImportTransformerGlobalIdentifier="undefined"!=typeof window?window:"undefined"!=typeof self?self:"undefined"!=typeof global?global:{};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}}_exports.displayChooser=(modalPromise,sectionModules,partialFavourite,footerData)=>{window.console.warn("The displayChooser function is deprecated. Please displayActivityChooserModal instead."),modalPromise.then((modal=>(new ActivityChooserDialogue(modal,sectionModules,footerData),modal))).catch(_notification.default.exception)};class ActivityChooserDialogue{constructor(modal,modulesData,footerData){this.modal=modal,this.dialogueDom=null,this.footerData=footerData,this.exporter=new _exporter.default,this.selectedModule=null,this.isFavouriteTabDirty=!1,this.mappedModules=new Map,modulesData.forEach((module=>{this.mappedModules.set(module.componentname+"_"+module.link,module)})),this.init()}async init(){await this.modal.getBodyPromise(),await this.modal.getFooterPromise(),this.dialogueDom=new _dialoguedom.default(this,this.modal,this.exporter),this.registerModalListenerEvents(),this.setupKeyboardAccessibility(),this.modal.getRoot().on(ModalEvents.hidden,(()=>{this.modal.destroy()})),this.dialogueDom.unmarkAllChooserOptionAsSelected()}async registerModalListenerEvents(){const modalRoot=(0,_normalise.getFirst)(this.modal.getRoot());modalRoot.addEventListener("shown.bs.tab",(event=>{if(this.dialogueDom.unmarkAllChooserOptionAsSelected(),event.target.closest(_selectors.default.regions.allTabNav))return;const searchInput=this.dialogueDom.getSearchInputElement();searchInput.value.length>0&&(searchInput.value="",this.toggleSearchResultsView(searchInput.value))})),modalRoot.addEventListener("click",this.handleModalClick.bind(this)),modalRoot.addEventListener("dblclick",this.handleModalDoubleClick.bind(this));const searchInput=this.dialogueDom.getSearchInputElement();searchInput.addEventListener("input",(0,_utils.debounce)((()=>{this.toggleSearchResultsView(searchInput.value)}),300,{pending:!0})),this.dialogueDom.initBootstrapComponents(),modalRoot.addEventListener("shown.bs.tab",(event=>{event.relatedTarget&&this.dialogueDom.disableFocusAllChooserOptions(event.relatedTarget),this.dialogueDom.initActiveTabNavigation()})),modalRoot.addEventListener("shown.bs.tab",(()=>{this.isFavouriteTabDirty&&!this.dialogueDom.isFavoutiteTabActive()&&this.refreshFavouritesTabContent()})),this.dialogueDom.initActiveTabNavigation();(0,_normalise.getFirst)(await this.modal.getFooterPromise()).addEventListener("click",this.handleFooterClick.bind(this)),modalRoot.addEventListener("slide.bs.carousel",(event=>{void 0!==event.to&&(this.dialogueDom.toggleActiveFooter(0===event.to),this.dialogueDom.toggleBackButton(0!==event.to),this.dialogueDom.toggleAddButton(event.to<2))}))}async handleFooterClick(event){if(event.target.closest(_selectors.default.actions.addSelectedChooserOption)&&this.submitAddSelectedModule(),event.target.closest(_selectors.default.regions.activeFooter)&&!0===this.footerData.footer){const footerjs=await(pluginName=this.footerData.customfooterjs,"function"==typeof _systemImportTransformerGlobalIdentifier.define&&_systemImportTransformerGlobalIdentifier.define.amd?new Promise((function(resolve,reject){_systemImportTransformerGlobalIdentifier.require([pluginName],resolve,reject)})):"undefined"!=typeof module&&module.exports&&"undefined"!=typeof require||"undefined"!=typeof module&&module.component&&_systemImportTransformerGlobalIdentifier.require&&"component"===_systemImportTransformerGlobalIdentifier.require.loader?Promise.resolve(require(pluginName)):Promise.resolve(_systemImportTransformerGlobalIdentifier[pluginName]));await footerjs.footerClickListener(event,this.footerData,this.modal)}var pluginName}async handleModalClick(event){const target=event.target;if(target.closest(_selectors.default.actions.optionActions.showSummary))this.handleShowSummary(target);else if(target.closest(_selectors.default.actions.optionActions.manageFavourite))await this.handleFavouriteClick(target);else if(target.matches(_selectors.default.actions.closeOption))this.dialogueDom.hideModuleHelp(target.dataset.modname);else{if(!target.closest(_selectors.default.actions.clearSearch))return target.closest(_selectors.default.regions.chooserOption.container)?(event.preventDefault(),void this.handleOptionSelection(target)):void 0;this.handleClearSearch()}}handleModalDoubleClick(event){null!==this.dialogueDom.getClosestChooserOption(event.target)&&this.submitAddSelectedModule(this.selectedModule)}handleShowSummary(target){const moduleName=this.dialogueDom.getClosestChooserOption(target).dataset.modname,moduleData=this.mappedModules.get(moduleName);this.handleOptionSelection(target),moduleData.showFooter=this.modal.hasFooterContent(),this.dialogueDom.setBackButtonModuleData(moduleData),this.dialogueDom.showModuleHelp(moduleData,this.modal)}async handleFavouriteClick(target){const caller=target.closest(_selectors.default.actions.optionActions.manageFavourite),id=caller.dataset.id,name=caller.dataset.name,internal=caller.dataset.internal;"true"===caller.dataset.favourited?(await Repository.unfavouriteModule(name,id),this.updateFavouriteItemValue(internal,!1)):(await Repository.favouriteModule(name,id),this.updateFavouriteItemValue(internal,!0))}handleClearSearch(){const searchInput=this.dialogueDom.getSearchInputElement();searchInput.value="",searchInput.focus(),this.toggleSearchResultsView(searchInput.value)}handleOptionSelection(target){const option=this.dialogueDom.getClosestChooserOption(target);null!==option&&(this.selectedModule=option,this.dialogueDom.markChooserOptionAsSelected(option,(0,_normalise.getFirst)(this.modal.getFooter())))}submitAddSelectedModule(){let newSelectedModule=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;newSelectedModule&&this.handleOptionSelection(newSelectedModule),null!==this.selectedModule&&(window.location.href=this.dialogueDom.getChooserOptionUrl(this.selectedModule))}setupKeyboardAccessibility(){const mainElement=(0,_normalise.getFirst)(this.modal.getModal());mainElement.tabIndex=-1,mainElement.addEventListener("keydown",(e=>{const currentOption=this.dialogueDom.getClosestChooserOption(e.target);null!==currentOption&&(e.keyCode!==_key_codes.enter&&e.keyCode!==_key_codes.space||!e.target.closest(_selectors.default.regions.chooserOption.actions))&&(e.keyCode===_key_codes.space&&(e.preventDefault(),this.handleOptionSelection(currentOption)),e.keyCode===_key_codes.enter&&(e.preventDefault(),this.submitAddSelectedModule(currentOption)),e.keyCode===_key_codes.arrowRight&&(e.preventDefault(),this.dialogueDom.focusNextChooserOption(currentOption)),e.keyCode===_key_codes.arrowLeft&&(e.preventDefault(),this.dialogueDom.focusPreviousChooserOption(currentOption)),e.keyCode===_key_codes.home&&(e.preventDefault(),this.dialogueDom.focusFirstChooserOption(currentOption)),e.keyCode===_key_codes.end&&(e.preventDefault(),this.dialogueDom.focusLastChooserOption(currentOption)))}))}async toggleSearchResultsView(searchQuery){const searchResultsData=this.searchModules(searchQuery);searchQuery.length>0?(await this.dialogueDom.refreshSearchResults(searchResultsData),this.dialogueDom.showAllActivitiesTab(!0)):this.dialogueDom.cleanSearchResults()}searchModules(searchTerm){if(""===searchTerm)return this.mappedModules;searchTerm=searchTerm.toLowerCase();const searchResults=[];return this.mappedModules.forEach((activity=>{const activityName=activity.title.toLowerCase(),activityDesc=activity.help.toLowerCase();(activityName.includes(searchTerm)||activityDesc.includes(searchTerm))&&searchResults.push(activity)})),searchResults}async updateFavouriteItemValue(internal,favourite){const moduleItem=this.mappedModules.find((_ref=>{let{name:name}=_ref;return name===internal}));moduleItem&&(moduleItem.favourite=favourite,this.dialogueDom.updateItemStarredIcons(internal,favourite),this.dialogueDom.isFavoutiteTabActive()?this.isFavouriteTabDirty=!0:this.refreshFavouritesTabContent())}async refreshFavouritesTabContent(){this.isFavouriteTabDirty=!1;const favouriteCount=this.mappedModules.filter((mod=>!0===mod.favourite)).size;this.dialogueDom.toggleFavouriteTabDisplay(favouriteCount>0),await this.dialogueDom.refreshFavouritesTabContent(this.mappedModules)}}})); +define("core_course/local/activitychooser/dialogue",["exports","core/utils","core_course/local/activitychooser/dialoguedom","core/key_codes","core_course/local/activitychooser/exporter","core/normalise","core/str","core/modal","core/modal_events","core/notification","core_course/local/activitychooser/repository","core_course/local/activitychooser/selectors","core/templates"],(function(_exports,_utils,_dialoguedom,_key_codes,_exporter,_normalise,_str,_modal,ModalEvents,_notification,Repository,_selectors,Templates){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.displayActivityChooserModal=async function(footerDataPromise,modulesDataPromise){let bodyPromiseResolver;const bodyPromise=new Promise((resolve=>{bodyPromiseResolver=resolve})),exporter=new _exporter.default,footerData=await footerDataPromise,footerPromise=Templates.render("core_course/local/activitychooser/footer",exporter.getFooterData(footerData)),sectionModal=_modal.default.create({body:bodyPromise,title:(0,_str.getString)("addresourceoractivity"),footer:footerPromise,large:!0,scrollable:!1,templateContext:{classes:"modchooser"},show:!0});try{const modulesData=await modulesDataPromise;if(!modulesData)return;const modal=await sectionModal;new ActivityChooserDialogue(modal,modulesData,footerData);const templateData=await exporter.getModChooserTemplateData(modulesData);bodyPromiseResolver(await Templates.render("core_course/activitychooser",templateData))}catch(error){const errorTemplateData={errormessage:error.message};return void bodyPromiseResolver(await Templates.render("core_course/local/activitychooser/error",errorTemplateData))}},_exports.displayChooser=void 0,_dialoguedom=_interopRequireDefault(_dialoguedom),_exporter=_interopRequireDefault(_exporter),_modal=_interopRequireDefault(_modal),ModalEvents=_interopRequireWildcard(ModalEvents),_notification=_interopRequireDefault(_notification),Repository=_interopRequireWildcard(Repository),_selectors=_interopRequireDefault(_selectors),Templates=_interopRequireWildcard(Templates);var _systemImportTransformerGlobalIdentifier="undefined"!=typeof window?window:"undefined"!=typeof self?self:"undefined"!=typeof global?global:{};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}}_exports.displayChooser=(modalPromise,sectionModules,partialFavourite,footerData)=>{window.console.warn("The displayChooser function is deprecated. Please displayActivityChooserModal instead."),modalPromise.then((modal=>(new ActivityChooserDialogue(modal,sectionModules,footerData),modal))).catch(_notification.default.exception)};class ActivityChooserDialogue{constructor(modal,modulesData,footerData){this.modal=modal,this.dialogueDom=null,this.footerData=footerData,this.exporter=new _exporter.default,this.selectedModule=null,this.isFavouriteTabDirty=!1,this.mappedModules=new Map,modulesData.forEach((module=>{this.mappedModules.set(module.componentname+"_"+module.link,module)})),this.init()}async init(){await this.modal.getBodyPromise(),await this.modal.getFooterPromise(),this.dialogueDom=new _dialoguedom.default(this,this.modal,this.exporter),this.registerModalListenerEvents(),this.setupKeyboardAccessibility(),this.modal.getRoot().on(ModalEvents.hidden,(()=>{this.modal.destroy()})),this.dialogueDom.unmarkAllChooserOptionAsSelected()}async registerModalListenerEvents(){const modalRoot=(0,_normalise.getFirst)(this.modal.getRoot());modalRoot.addEventListener("shown.bs.tab",(event=>{if(this.dialogueDom.unmarkAllChooserOptionAsSelected(),event.target.closest(_selectors.default.regions.allTabNav))return;const searchInput=this.dialogueDom.getSearchInputElement();searchInput.value.length>0&&(searchInput.value="",this.toggleSearchResultsView(searchInput.value))})),modalRoot.addEventListener("click",this.handleModalClick.bind(this)),modalRoot.addEventListener("dblclick",this.handleModalDoubleClick.bind(this));const searchInput=this.dialogueDom.getSearchInputElement();searchInput.addEventListener("input",(0,_utils.debounce)((()=>{this.toggleSearchResultsView(searchInput.value)}),300,{pending:!0})),this.dialogueDom.initBootstrapComponents(),modalRoot.addEventListener("shown.bs.tab",(event=>{event.relatedTarget&&this.dialogueDom.disableFocusAllChooserOptions(event.relatedTarget),this.dialogueDom.initActiveTabNavigation()})),modalRoot.addEventListener("shown.bs.tab",(()=>{this.isFavouriteTabDirty&&!this.dialogueDom.isFavoutiteTabActive()&&this.refreshFavouritesTabContent()})),this.dialogueDom.initActiveTabNavigation();(0,_normalise.getFirst)(await this.modal.getFooterPromise()).addEventListener("click",this.handleFooterClick.bind(this)),modalRoot.addEventListener("slide.bs.carousel",(event=>{void 0!==event.to&&(this.dialogueDom.toggleActiveFooter(0===event.to),this.dialogueDom.toggleBackButton(0!==event.to),this.dialogueDom.toggleAddButton(event.to<2))}))}async handleFooterClick(event){if(event.target.closest(_selectors.default.actions.addSelectedChooserOption)&&this.submitAddSelectedModule(),event.target.closest(_selectors.default.regions.activeFooter)&&!0===this.footerData.footer){const footerjs=await(pluginName=this.footerData.customfooterjs,"function"==typeof _systemImportTransformerGlobalIdentifier.define&&_systemImportTransformerGlobalIdentifier.define.amd?new Promise((function(resolve,reject){_systemImportTransformerGlobalIdentifier.require([pluginName],resolve,reject)})):"undefined"!=typeof module&&module.exports&&"undefined"!=typeof require||"undefined"!=typeof module&&module.component&&_systemImportTransformerGlobalIdentifier.require&&"component"===_systemImportTransformerGlobalIdentifier.require.loader?Promise.resolve(require(pluginName)):Promise.resolve(_systemImportTransformerGlobalIdentifier[pluginName]));await footerjs.footerClickListener(event,this.footerData,this.modal)}var pluginName}async handleModalClick(event){event.preventDefault();const target=event.target;if(target.closest(_selectors.default.actions.optionActions.showSummary))this.handleShowSummary(target);else if(target.closest(_selectors.default.actions.optionActions.manageFavourite))await this.handleFavouriteClick(target);else if(target.matches(_selectors.default.actions.closeOption))this.dialogueDom.hideModuleHelp(target.dataset.modname);else{if(!target.closest(_selectors.default.actions.clearSearch))return target.closest(_selectors.default.regions.chooserOption.info)?(event.preventDefault(),void this.handleOptionSelection(target)):void 0;this.handleClearSearch()}}handleModalDoubleClick(event){null!==this.dialogueDom.getClosestChooserOption(event.target)&&this.submitAddSelectedModule(this.selectedModule)}handleShowSummary(target){const moduleName=this.dialogueDom.getClosestChooserOption(target).dataset.modname,moduleData=this.mappedModules.get(moduleName);this.handleOptionSelection(target),moduleData.showFooter=this.modal.hasFooterContent(),this.dialogueDom.setBackButtonModuleData(moduleData),this.dialogueDom.showModuleHelp(moduleData,this.modal)}async handleFavouriteClick(target){const caller=target.closest(_selectors.default.actions.optionActions.manageFavourite),id=caller.dataset.id,name=caller.dataset.name,internal=caller.dataset.internal;"true"===caller.dataset.favourited?(await Repository.unfavouriteModule(name,id),this.updateFavouriteItemValue(internal,!1)):(await Repository.favouriteModule(name,id),this.updateFavouriteItemValue(internal,!0))}handleClearSearch(){const searchInput=this.dialogueDom.getSearchInputElement();searchInput.value="",searchInput.focus(),this.toggleSearchResultsView(searchInput.value)}handleOptionSelection(target){const option=this.dialogueDom.getClosestChooserOption(target);null!==option&&(this.selectedModule=option,this.dialogueDom.markChooserOptionAsSelected(option,(0,_normalise.getFirst)(this.modal.getFooter())))}submitAddSelectedModule(){let newSelectedModule=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;newSelectedModule&&this.handleOptionSelection(newSelectedModule),null!==this.selectedModule&&(window.location.href=this.dialogueDom.getChooserOptionUrl(this.selectedModule))}setupKeyboardAccessibility(){const mainElement=(0,_normalise.getFirst)(this.modal.getModal());mainElement.tabIndex=-1,mainElement.addEventListener("keydown",(e=>{const currentOption=this.dialogueDom.getClosestChooserOption(e.target);null!==currentOption&&(e.keyCode!==_key_codes.enter&&e.keyCode!==_key_codes.space||!e.target.closest(_selectors.default.regions.chooserOption.actions))&&(e.keyCode===_key_codes.space&&(e.preventDefault(),this.handleOptionSelection(currentOption)),e.keyCode===_key_codes.enter&&(e.preventDefault(),this.submitAddSelectedModule(currentOption)),e.keyCode===_key_codes.arrowRight&&(e.preventDefault(),this.dialogueDom.focusNextChooserOption(currentOption)),e.keyCode===_key_codes.arrowLeft&&(e.preventDefault(),this.dialogueDom.focusPreviousChooserOption(currentOption)),e.keyCode===_key_codes.home&&(e.preventDefault(),this.dialogueDom.focusFirstChooserOption(currentOption)),e.keyCode===_key_codes.end&&(e.preventDefault(),this.dialogueDom.focusLastChooserOption(currentOption)))}))}async toggleSearchResultsView(searchQuery){const searchResultsData=this.searchModules(searchQuery);searchQuery.length>0?(await this.dialogueDom.refreshSearchResults(searchResultsData),this.dialogueDom.showAllActivitiesTab(!0)):this.dialogueDom.cleanSearchResults()}searchModules(searchTerm){if(""===searchTerm)return this.mappedModules;searchTerm=searchTerm.toLowerCase();const searchResults=[];return this.mappedModules.forEach((activity=>{const activityName=activity.title.toLowerCase(),activityDesc=activity.help.toLowerCase();(activityName.includes(searchTerm)||activityDesc.includes(searchTerm))&&searchResults.push(activity)})),searchResults}async updateFavouriteItemValue(internal,favourite){const moduleItem=this.mappedModules.find((_ref=>{let{name:name}=_ref;return name===internal}));moduleItem&&(moduleItem.favourite=favourite,this.dialogueDom.updateItemStarredIcons(internal,favourite),this.dialogueDom.isFavoutiteTabActive()?this.isFavouriteTabDirty=!0:this.refreshFavouritesTabContent())}async refreshFavouritesTabContent(){this.isFavouriteTabDirty=!1;const favouriteCount=this.mappedModules.filter((mod=>!0===mod.favourite)).size;this.dialogueDom.toggleFavouriteTabDisplay(favouriteCount>0),await this.dialogueDom.refreshFavouritesTabContent(this.mappedModules)}}})); //# sourceMappingURL=dialogue.min.js.map \ No newline at end of file diff --git a/public/course/amd/build/local/activitychooser/dialogue.min.js.map b/public/course/amd/build/local/activitychooser/dialogue.min.js.map index dda3be353ce..35af7ccc1be 100644 --- a/public/course/amd/build/local/activitychooser/dialogue.min.js.map +++ b/public/course/amd/build/local/activitychooser/dialogue.min.js.map @@ -1 +1 @@ -{"version":3,"file":"dialogue.min.js","sources":["../../../src/local/activitychooser/dialogue.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 type of dialogue used as for choosing options.\n *\n * @module core_course/local/activitychooser/dialogue\n * @copyright 2019 Mihail Geshoski \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n// import {addIconToContainer} from 'core/loadingicon';\nimport {debounce} from 'core/utils';\nimport DialogueDom from 'core_course/local/activitychooser/dialoguedom';\nimport {end, arrowLeft, arrowRight, home, enter, space} from 'core/key_codes';\nimport Exporter from 'core_course/local/activitychooser/exporter';\nimport {getFirst} from 'core/normalise';\nimport {getString} from 'core/str';\nimport Modal from 'core/modal';\nimport * as ModalEvents from 'core/modal_events';\nimport Notification from 'core/notification';\nimport * as Repository from 'core_course/local/activitychooser/repository';\nimport selectors from 'core_course/local/activitychooser/selectors';\nimport * as Templates from 'core/templates';\nconst getPlugin = pluginName => import(pluginName);\n\n/**\n * Display the activity chooser modal.\n *\n * @method displayActivityChooser\n * @param {Promise} footerDataPromise Promise for the footer data.\n * @param {Promise} modulesDataPromise Promise for the modules data.\n */\nexport async function displayActivityChooserModal(\n footerDataPromise,\n modulesDataPromise,\n) {\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 const exporter = new Exporter();\n\n const footerData = await footerDataPromise;\n\n const footerPromise = Templates.render(\n 'core_course/local/activitychooser/footer',\n exporter.getFooterData(footerData),\n );\n\n const sectionModal = Modal.create({\n body: bodyPromise,\n title: getString('addresourceoractivity'),\n footer: footerPromise,\n large: true,\n scrollable: false,\n templateContext: {\n classes: 'modchooser'\n },\n show: true,\n });\n\n try {\n const modulesData = await modulesDataPromise;\n\n if (!modulesData) {\n return;\n }\n\n const modal = await sectionModal;\n new ActivityChooserDialogue(modal, modulesData, footerData);\n\n const templateData = await exporter.getModChooserTemplateData(modulesData);\n bodyPromiseResolver(await Templates.render('core_course/activitychooser', templateData));\n } catch (error) {\n const errorTemplateData = {\n 'errormessage': error.message\n };\n bodyPromiseResolver(\n await Templates.render('core_course/local/activitychooser/error', errorTemplateData)\n );\n return;\n }\n}\n\n/**\n * Display the module chooser.\n *\n * @deprecated since Moodle 5.1\n * @todo Remove the method in Moodle 6.0 (MDL-85655).\n * @method displayChooser\n * @param {Promise} modalPromise Our created modal for the section\n * @param {Array} sectionModules An array of all of the built module information\n * @param {Function} partialFavourite Partially applied function we need to manage favourite status\n * @param {Object} footerData Our base footer object.\n */\nexport const displayChooser = (modalPromise, sectionModules, partialFavourite, footerData) => {\n window.console.warn(\n 'The displayChooser function is deprecated. ' +\n 'Please displayActivityChooserModal instead.'\n );\n\n // Register event listeners.\n modalPromise.then(modal => {\n new ActivityChooserDialogue(modal, sectionModules, footerData);\n return modal;\n }).catch(Notification.exception);\n};\n\n/**\n * Activity Chooser Dialogue class.\n *\n * @private\n */\nclass ActivityChooserDialogue {\n /**\n * Constructor for the ActivityChooserDialogue class.\n * @param {Modal} modal The modal object.\n * @param {Object} modulesData The data for the modules.\n * @param {Object} footerData The data for the footer.\n */\n constructor(modal, modulesData, footerData) {\n this.modal = modal;\n this.dialogueDom = null; // We cannot init until we have the modal body loaded.\n this.footerData = footerData;\n this.exporter = new Exporter();\n this.selectedModule = null;\n // This attribute marks when the tab content is dirty and needs to be refreshed when the user changes the tab.\n // We don't want the content to be updated while the user is managing their favourites.\n this.isFavouriteTabDirty = false;\n // Make a map so we can quickly fetch a specific module's object for either rendering or searching.\n this.mappedModules = new Map();\n modulesData.forEach((module) => {\n this.mappedModules.set(module.componentname + '_' + module.link, module);\n });\n this.init();\n }\n\n /**\n * Initialise the activity chooser dialogue.\n *\n * @return {Promise} A promise that resolves when the modal is ready.\n */\n async init() {\n await this.modal.getBodyPromise();\n await this.modal.getFooterPromise();\n this.dialogueDom = new DialogueDom(this, this.modal, this.exporter);\n this.registerModalListenerEvents();\n this.setupKeyboardAccessibility();\n // We want to focus on the action select when the dialog is closed.\n this.modal.getRoot().on(ModalEvents.hidden, () => {\n this.modal.destroy();\n });\n // When modal is open the add button should be disabled because there is no activity selected.\n this.dialogueDom.unmarkAllChooserOptionAsSelected();\n }\n\n /**\n * Register chooser related event listeners.\n *\n * @returns {Promise} A promise that resolves when events are registered\n */\n async registerModalListenerEvents() {\n const modalRoot = getFirst(this.modal.getRoot());\n\n // Changing the tab should cancel any active search.\n modalRoot.addEventListener(\n 'shown.bs.tab',\n (event) => {\n this.dialogueDom.unmarkAllChooserOptionAsSelected();\n // The all tab has the search result, so we do not want to clear the search input.\n if (event.target.closest(selectors.regions.allTabNav)) {\n return;\n }\n const searchInput = this.dialogueDom.getSearchInputElement();\n if (searchInput.value.length > 0) {\n searchInput.value = \"\";\n this.toggleSearchResultsView(searchInput.value);\n }\n },\n );\n\n // Add the listener for clicks on the full modal.\n modalRoot.addEventListener(\n 'click',\n this.handleModalClick.bind(this),\n );\n modalRoot.addEventListener(\n 'dblclick',\n this.handleModalDoubleClick.bind(this),\n );\n\n // Add a listener for an input change in the activity chooser's search bar.\n const searchInput = this.dialogueDom.getSearchInputElement();\n searchInput.addEventListener(\n 'input',\n debounce(\n () => {\n this.toggleSearchResultsView(searchInput.value);\n },\n 300,\n {pending: true},\n ),\n );\n\n this.dialogueDom.initBootstrapComponents();\n\n // Handle focus when a new tab is shown.\n modalRoot.addEventListener('shown.bs.tab', (event) => {\n if (event.relatedTarget) {\n this.dialogueDom.disableFocusAllChooserOptions(event.relatedTarget);\n }\n this.dialogueDom.initActiveTabNavigation();\n });\n\n // Update the favourite tab content when the user changes the tab.\n modalRoot.addEventListener('shown.bs.tab', () => {\n if (this.isFavouriteTabDirty && !this.dialogueDom.isFavoutiteTabActive()) {\n this.refreshFavouritesTabContent();\n }\n });\n\n this.dialogueDom.initActiveTabNavigation();\n\n const modalFooter = getFirst(await this.modal.getFooterPromise());\n\n // Add the listener for clicks on the footer.\n modalFooter.addEventListener(\n 'click',\n this.handleFooterClick.bind(this),\n );\n\n // Adapt modal footer depending on the displayed carousel page.\n modalRoot.addEventListener('slide.bs.carousel', (event) => {\n if (event.to === undefined) {\n return;\n }\n // The boostrap carousel event.to contains the index of the newly active item.\n // The zero index is the chooser options, the first index is the module help,\n // any other index are custom footer pages.\n this.dialogueDom.toggleActiveFooter(event.to === 0);\n this.dialogueDom.toggleBackButton(event.to !== 0);\n this.dialogueDom.toggleAddButton(event.to < 2);\n });\n }\n\n /**\n * Handle the click event on the footer of the modal.\n *\n * @param {Object} event The event object\n * @return {Promise} A promise that resolves when the event is handled\n */\n async handleFooterClick(event) {\n if (event.target.closest(selectors.actions.addSelectedChooserOption)) {\n this.submitAddSelectedModule();\n }\n\n if (\n event.target.closest(selectors.regions.activeFooter)\n && this.footerData.footer === true\n ) {\n const footerjs = await getPlugin(this.footerData.customfooterjs);\n await footerjs.footerClickListener(event, this.footerData, this.modal);\n }\n }\n\n /**\n * Modal click handler.\n *\n * @param {Object} event The event object\n * @return {Promise} A promise that resolves when the event is handled\n */\n async handleModalClick(event) {\n const target = event.target;\n\n if (target.closest(selectors.actions.optionActions.showSummary)) {\n this.handleShowSummary(target);\n return;\n }\n\n if (target.closest(selectors.actions.optionActions.manageFavourite)) {\n await this.handleFavouriteClick(target);\n return;\n }\n\n // From the help screen go back to the module overview.\n if (target.matches(selectors.actions.closeOption)) {\n this.dialogueDom.hideModuleHelp(target.dataset.modname);\n return;\n }\n\n // The \"clear search\" button is triggered.\n if (target.closest(selectors.actions.clearSearch)) {\n this.handleClearSearch();\n return;\n }\n\n if (target.closest(selectors.regions.chooserOption.container)) {\n event.preventDefault();\n this.handleOptionSelection(target);\n return;\n }\n }\n\n /**\n * Handle the double click event on the modal.\n * @param {Object} event The event object\n */\n handleModalDoubleClick(event) {\n const option = this.dialogueDom.getClosestChooserOption(event.target);\n if (option !== null) {\n this.submitAddSelectedModule(this.selectedModule);\n }\n }\n\n /**\n * Show the summary of a module when the user clicks on the \"show summary\" button.\n *\n * @param {HTMLElement} target The target element that triggered the event\n */\n handleShowSummary(target) {\n const module = this.dialogueDom.getClosestChooserOption(target);\n const moduleName = module.dataset.modname;\n const moduleData = this.mappedModules.get(moduleName);\n // We select the module now. This way the back button will keep the module selected.\n this.handleOptionSelection(target);\n // We need to know if the overall modal has a footer so we know when to show a real / vs fake footer.\n moduleData.showFooter = this.modal.hasFooterContent();\n this.dialogueDom.setBackButtonModuleData(moduleData);\n this.dialogueDom.showModuleHelp(moduleData, this.modal);\n }\n\n /**\n * Handle the favourite state of a module when the user clicks on the \"starred\" button.\n *\n * @param {HTMLElement} target The target element that triggered the event\n * @return {Promise} A promise that resolves when the event is handled\n */\n async handleFavouriteClick(target) {\n const caller = target.closest(selectors.actions.optionActions.manageFavourite);\n const id = caller.dataset.id;\n const name = caller.dataset.name;\n const internal = caller.dataset.internal;\n const isFavourite = caller.dataset.favourited;\n\n // Switch on fave or not.\n if (isFavourite === 'true') {\n await Repository.unfavouriteModule(name, id);\n this.updateFavouriteItemValue(internal, false);\n } else {\n await Repository.favouriteModule(name, id);\n this.updateFavouriteItemValue(internal, true);\n }\n }\n\n /**\n * Handle a clear search action.\n */\n handleClearSearch() {\n const searchInput = this.dialogueDom.getSearchInputElement();\n searchInput.value = \"\";\n searchInput.focus();\n this.toggleSearchResultsView(searchInput.value);\n }\n\n /**\n * Handle the click on a chooser option.\n *\n * @param {HTMLElement} target The target element that triggered the event\n */\n handleOptionSelection(target) {\n const option = this.dialogueDom.getClosestChooserOption(target);\n if (option === null) {\n return;\n }\n this.selectedModule = option;\n this.dialogueDom.markChooserOptionAsSelected(option, getFirst(this.modal.getFooter()));\n }\n\n /**\n * Submit the selected module to the chooser.\n *\n * This method will redirect the user to the URL of the selected module, if one is selected.\n *\n * @param {HTMLElement} newSelectedModule optional new selected module element.\n * @return {void}\n */\n submitAddSelectedModule(newSelectedModule = null) {\n if (newSelectedModule) {\n this.handleOptionSelection(newSelectedModule);\n }\n if (this.selectedModule === null) {\n return;\n }\n window.location.href = this.dialogueDom.getChooserOptionUrl(this.selectedModule);\n }\n\n /**\n * Set up our tabindex information across the chooser.\n *\n * @method setupKeyboardAccessibility\n */\n setupKeyboardAccessibility() {\n const mainElement = getFirst(this.modal.getModal());\n\n mainElement.tabIndex = -1;\n\n mainElement.addEventListener('keydown', (e) => {\n const currentOption = this.dialogueDom.getClosestChooserOption(e.target);\n if (currentOption === null) {\n return;\n }\n\n if (e.keyCode === enter || e.keyCode === space) {\n // Check first if the target is an internal control button (favourite or help).\n // If that is the case, the regular click will handle the event.\n if (e.target.closest(selectors.regions.chooserOption.actions)) {\n return;\n }\n }\n\n if (e.keyCode === space) {\n e.preventDefault();\n this.handleOptionSelection(currentOption);\n }\n if (e.keyCode === enter) {\n e.preventDefault();\n this.submitAddSelectedModule(currentOption);\n }\n if (e.keyCode === arrowRight) {\n e.preventDefault();\n this.dialogueDom.focusNextChooserOption(currentOption);\n }\n if (e.keyCode === arrowLeft) {\n e.preventDefault();\n this.dialogueDom.focusPreviousChooserOption(currentOption);\n }\n if (e.keyCode === home) {\n e.preventDefault();\n this.dialogueDom.focusFirstChooserOption(currentOption);\n }\n if (e.keyCode === end) {\n e.preventDefault();\n this.dialogueDom.focusLastChooserOption(currentOption);\n }\n });\n }\n\n /**\n * Toggle (display/hide) the search results depending on the value of the search query\n *\n * @method toggleSearchResultsView\n * @param {String} searchQuery The search query\n */\n async toggleSearchResultsView(searchQuery) {\n const searchResultsData = this.searchModules(searchQuery);\n\n if (searchQuery.length > 0) {\n await this.dialogueDom.refreshSearchResults(searchResultsData);\n this.dialogueDom.showAllActivitiesTab(true);\n } else {\n this.dialogueDom.cleanSearchResults();\n }\n }\n\n /**\n * Return the list of modules which have a name or description that matches the given search term.\n *\n * @method searchModules\n * @param {String} searchTerm The search term to match\n * @return {Array}\n */\n searchModules(searchTerm) {\n if (searchTerm === '') {\n return this.mappedModules;\n }\n searchTerm = searchTerm.toLowerCase();\n const searchResults = [];\n this.mappedModules.forEach((activity) => {\n const activityName = activity.title.toLowerCase();\n const activityDesc = activity.help.toLowerCase();\n if (activityName.includes(searchTerm) || activityDesc.includes(searchTerm)) {\n searchResults.push(activity);\n }\n });\n\n return searchResults;\n }\n\n /**\n * Update the favourite item value in the mapped modules.\n *\n * @param {String} internal The internal name of the module.\n * @param {Boolean} favourite Whether the module is a favourite or not.\n * @return {Promise} A promise that resolves when the item is updated.\n */\n async updateFavouriteItemValue(internal, favourite) {\n const moduleItem = this.mappedModules.find(({name}) => name === internal);\n if (!moduleItem) {\n return;\n }\n moduleItem.favourite = favourite;\n\n this.dialogueDom.updateItemStarredIcons(internal, favourite);\n\n if (this.dialogueDom.isFavoutiteTabActive()) {\n this.isFavouriteTabDirty = true;\n } else {\n this.refreshFavouritesTabContent();\n }\n }\n\n /**\n * Refresh the favourites tab content.\n *\n * Note: this method will also hide the favourites tab if there are no favourite modules\n * to keep the modal consistent.\n *\n * @return {Promise} A promise that resolves when the content is refreshed.\n */\n async refreshFavouritesTabContent() {\n this.isFavouriteTabDirty = false;\n const favouriteCount = this.mappedModules.filter(mod => mod.favourite === true).size;\n this.dialogueDom.toggleFavouriteTabDisplay(favouriteCount > 0);\n await this.dialogueDom.refreshFavouritesTabContent(this.mappedModules);\n }\n}\n"],"names":["footerDataPromise","modulesDataPromise","bodyPromiseResolver","bodyPromise","Promise","resolve","exporter","Exporter","footerData","footerPromise","Templates","render","getFooterData","sectionModal","Modal","create","body","title","footer","large","scrollable","templateContext","classes","show","modulesData","modal","ActivityChooserDialogue","templateData","getModChooserTemplateData","error","errorTemplateData","message","modalPromise","sectionModules","partialFavourite","window","console","warn","then","catch","Notification","exception","constructor","dialogueDom","selectedModule","isFavouriteTabDirty","mappedModules","Map","forEach","module","set","componentname","link","init","this","getBodyPromise","getFooterPromise","DialogueDom","registerModalListenerEvents","setupKeyboardAccessibility","getRoot","on","ModalEvents","hidden","destroy","unmarkAllChooserOptionAsSelected","modalRoot","addEventListener","event","target","closest","selectors","regions","allTabNav","searchInput","getSearchInputElement","value","length","toggleSearchResultsView","handleModalClick","bind","handleModalDoubleClick","pending","initBootstrapComponents","relatedTarget","disableFocusAllChooserOptions","initActiveTabNavigation","isFavoutiteTabActive","refreshFavouritesTabContent","handleFooterClick","undefined","to","toggleActiveFooter","toggleBackButton","toggleAddButton","actions","addSelectedChooserOption","submitAddSelectedModule","activeFooter","footerjs","pluginName","customfooterjs","footerClickListener","optionActions","showSummary","handleShowSummary","manageFavourite","handleFavouriteClick","matches","closeOption","hideModuleHelp","dataset","modname","clearSearch","chooserOption","container","preventDefault","handleOptionSelection","handleClearSearch","getClosestChooserOption","moduleName","moduleData","get","showFooter","hasFooterContent","setBackButtonModuleData","showModuleHelp","caller","id","name","internal","favourited","Repository","unfavouriteModule","updateFavouriteItemValue","favouriteModule","focus","option","markChooserOptionAsSelected","getFooter","newSelectedModule","location","href","getChooserOptionUrl","mainElement","getModal","tabIndex","e","currentOption","keyCode","enter","space","arrowRight","focusNextChooserOption","arrowLeft","focusPreviousChooserOption","home","focusFirstChooserOption","end","focusLastChooserOption","searchQuery","searchResultsData","searchModules","refreshSearchResults","showAllActivitiesTab","cleanSearchResults","searchTerm","toLowerCase","searchResults","activity","activityName","activityDesc","help","includes","push","favourite","moduleItem","find","_ref","updateItemStarredIcons","favouriteCount","filter","mod","size","toggleFavouriteTabDisplay"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"dialogue.min.js","sources":["../../../src/local/activitychooser/dialogue.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 type of dialogue used as for choosing options.\n *\n * @module core_course/local/activitychooser/dialogue\n * @copyright 2019 Mihail Geshoski \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n// import {addIconToContainer} from 'core/loadingicon';\nimport {debounce} from 'core/utils';\nimport DialogueDom from 'core_course/local/activitychooser/dialoguedom';\nimport {end, arrowLeft, arrowRight, home, enter, space} from 'core/key_codes';\nimport Exporter from 'core_course/local/activitychooser/exporter';\nimport {getFirst} from 'core/normalise';\nimport {getString} from 'core/str';\nimport Modal from 'core/modal';\nimport * as ModalEvents from 'core/modal_events';\nimport Notification from 'core/notification';\nimport * as Repository from 'core_course/local/activitychooser/repository';\nimport selectors from 'core_course/local/activitychooser/selectors';\nimport * as Templates from 'core/templates';\nconst getPlugin = pluginName => import(pluginName);\n\n/**\n * Display the activity chooser modal.\n *\n * @method displayActivityChooser\n * @param {Promise} footerDataPromise Promise for the footer data.\n * @param {Promise} modulesDataPromise Promise for the modules data.\n */\nexport async function displayActivityChooserModal(\n footerDataPromise,\n modulesDataPromise,\n) {\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 const exporter = new Exporter();\n\n const footerData = await footerDataPromise;\n\n const footerPromise = Templates.render(\n 'core_course/local/activitychooser/footer',\n exporter.getFooterData(footerData),\n );\n\n const sectionModal = Modal.create({\n body: bodyPromise,\n title: getString('addresourceoractivity'),\n footer: footerPromise,\n large: true,\n scrollable: false,\n templateContext: {\n classes: 'modchooser'\n },\n show: true,\n });\n\n try {\n const modulesData = await modulesDataPromise;\n\n if (!modulesData) {\n return;\n }\n\n const modal = await sectionModal;\n new ActivityChooserDialogue(modal, modulesData, footerData);\n\n const templateData = await exporter.getModChooserTemplateData(modulesData);\n bodyPromiseResolver(await Templates.render('core_course/activitychooser', templateData));\n } catch (error) {\n const errorTemplateData = {\n 'errormessage': error.message\n };\n bodyPromiseResolver(\n await Templates.render('core_course/local/activitychooser/error', errorTemplateData)\n );\n return;\n }\n}\n\n/**\n * Display the module chooser.\n *\n * @deprecated since Moodle 5.1\n * @todo Remove the method in Moodle 6.0 (MDL-85655).\n * @method displayChooser\n * @param {Promise} modalPromise Our created modal for the section\n * @param {Array} sectionModules An array of all of the built module information\n * @param {Function} partialFavourite Partially applied function we need to manage favourite status\n * @param {Object} footerData Our base footer object.\n */\nexport const displayChooser = (modalPromise, sectionModules, partialFavourite, footerData) => {\n window.console.warn(\n 'The displayChooser function is deprecated. ' +\n 'Please displayActivityChooserModal instead.'\n );\n\n // Register event listeners.\n modalPromise.then(modal => {\n new ActivityChooserDialogue(modal, sectionModules, footerData);\n return modal;\n }).catch(Notification.exception);\n};\n\n/**\n * Activity Chooser Dialogue class.\n *\n * @private\n */\nclass ActivityChooserDialogue {\n /**\n * Constructor for the ActivityChooserDialogue class.\n * @param {Modal} modal The modal object.\n * @param {Object} modulesData The data for the modules.\n * @param {Object} footerData The data for the footer.\n */\n constructor(modal, modulesData, footerData) {\n this.modal = modal;\n this.dialogueDom = null; // We cannot init until we have the modal body loaded.\n this.footerData = footerData;\n this.exporter = new Exporter();\n this.selectedModule = null;\n // This attribute marks when the tab content is dirty and needs to be refreshed when the user changes the tab.\n // We don't want the content to be updated while the user is managing their favourites.\n this.isFavouriteTabDirty = false;\n // Make a map so we can quickly fetch a specific module's object for either rendering or searching.\n this.mappedModules = new Map();\n modulesData.forEach((module) => {\n this.mappedModules.set(module.componentname + '_' + module.link, module);\n });\n this.init();\n }\n\n /**\n * Initialise the activity chooser dialogue.\n *\n * @return {Promise} A promise that resolves when the modal is ready.\n */\n async init() {\n await this.modal.getBodyPromise();\n await this.modal.getFooterPromise();\n this.dialogueDom = new DialogueDom(this, this.modal, this.exporter);\n this.registerModalListenerEvents();\n this.setupKeyboardAccessibility();\n // We want to focus on the action select when the dialog is closed.\n this.modal.getRoot().on(ModalEvents.hidden, () => {\n this.modal.destroy();\n });\n // When modal is open the add button should be disabled because there is no activity selected.\n this.dialogueDom.unmarkAllChooserOptionAsSelected();\n }\n\n /**\n * Register chooser related event listeners.\n *\n * @returns {Promise} A promise that resolves when events are registered\n */\n async registerModalListenerEvents() {\n const modalRoot = getFirst(this.modal.getRoot());\n\n // Changing the tab should cancel any active search.\n modalRoot.addEventListener(\n 'shown.bs.tab',\n (event) => {\n this.dialogueDom.unmarkAllChooserOptionAsSelected();\n // The all tab has the search result, so we do not want to clear the search input.\n if (event.target.closest(selectors.regions.allTabNav)) {\n return;\n }\n const searchInput = this.dialogueDom.getSearchInputElement();\n if (searchInput.value.length > 0) {\n searchInput.value = \"\";\n this.toggleSearchResultsView(searchInput.value);\n }\n },\n );\n\n // Add the listener for clicks on the full modal.\n modalRoot.addEventListener(\n 'click',\n this.handleModalClick.bind(this),\n );\n modalRoot.addEventListener(\n 'dblclick',\n this.handleModalDoubleClick.bind(this),\n );\n\n // Add a listener for an input change in the activity chooser's search bar.\n const searchInput = this.dialogueDom.getSearchInputElement();\n searchInput.addEventListener(\n 'input',\n debounce(\n () => {\n this.toggleSearchResultsView(searchInput.value);\n },\n 300,\n {pending: true},\n ),\n );\n\n this.dialogueDom.initBootstrapComponents();\n\n // Handle focus when a new tab is shown.\n modalRoot.addEventListener('shown.bs.tab', (event) => {\n if (event.relatedTarget) {\n this.dialogueDom.disableFocusAllChooserOptions(event.relatedTarget);\n }\n this.dialogueDom.initActiveTabNavigation();\n });\n\n // Update the favourite tab content when the user changes the tab.\n modalRoot.addEventListener('shown.bs.tab', () => {\n if (this.isFavouriteTabDirty && !this.dialogueDom.isFavoutiteTabActive()) {\n this.refreshFavouritesTabContent();\n }\n });\n\n this.dialogueDom.initActiveTabNavigation();\n\n const modalFooter = getFirst(await this.modal.getFooterPromise());\n\n // Add the listener for clicks on the footer.\n modalFooter.addEventListener(\n 'click',\n this.handleFooterClick.bind(this),\n );\n\n // Adapt modal footer depending on the displayed carousel page.\n modalRoot.addEventListener('slide.bs.carousel', (event) => {\n if (event.to === undefined) {\n return;\n }\n // The boostrap carousel event.to contains the index of the newly active item.\n // The zero index is the chooser options, the first index is the module help,\n // any other index are custom footer pages.\n this.dialogueDom.toggleActiveFooter(event.to === 0);\n this.dialogueDom.toggleBackButton(event.to !== 0);\n this.dialogueDom.toggleAddButton(event.to < 2);\n });\n }\n\n /**\n * Handle the click event on the footer of the modal.\n *\n * @param {Object} event The event object\n * @return {Promise} A promise that resolves when the event is handled\n */\n async handleFooterClick(event) {\n if (event.target.closest(selectors.actions.addSelectedChooserOption)) {\n this.submitAddSelectedModule();\n }\n\n if (\n event.target.closest(selectors.regions.activeFooter)\n && this.footerData.footer === true\n ) {\n const footerjs = await getPlugin(this.footerData.customfooterjs);\n await footerjs.footerClickListener(event, this.footerData, this.modal);\n }\n }\n\n /**\n * Modal click handler.\n *\n * @param {Object} event The event object\n * @return {Promise} A promise that resolves when the event is handled\n */\n async handleModalClick(event) {\n event.preventDefault();\n const target = event.target;\n\n if (target.closest(selectors.actions.optionActions.showSummary)) {\n this.handleShowSummary(target);\n return;\n }\n\n if (target.closest(selectors.actions.optionActions.manageFavourite)) {\n await this.handleFavouriteClick(target);\n return;\n }\n\n // From the help screen go back to the module overview.\n if (target.matches(selectors.actions.closeOption)) {\n this.dialogueDom.hideModuleHelp(target.dataset.modname);\n return;\n }\n\n // The \"clear search\" button is triggered.\n if (target.closest(selectors.actions.clearSearch)) {\n this.handleClearSearch();\n return;\n }\n\n if (target.closest(selectors.regions.chooserOption.info)) {\n event.preventDefault();\n this.handleOptionSelection(target);\n return;\n }\n }\n\n /**\n * Handle the double click event on the modal.\n * @param {Object} event The event object\n */\n handleModalDoubleClick(event) {\n const option = this.dialogueDom.getClosestChooserOption(event.target);\n if (option !== null) {\n this.submitAddSelectedModule(this.selectedModule);\n }\n }\n\n /**\n * Show the summary of a module when the user clicks on the \"show summary\" button.\n *\n * @param {HTMLElement} target The target element that triggered the event\n */\n handleShowSummary(target) {\n const module = this.dialogueDom.getClosestChooserOption(target);\n const moduleName = module.dataset.modname;\n const moduleData = this.mappedModules.get(moduleName);\n // We select the module now. This way the back button will keep the module selected.\n this.handleOptionSelection(target);\n // We need to know if the overall modal has a footer so we know when to show a real / vs fake footer.\n moduleData.showFooter = this.modal.hasFooterContent();\n this.dialogueDom.setBackButtonModuleData(moduleData);\n this.dialogueDom.showModuleHelp(moduleData, this.modal);\n }\n\n /**\n * Handle the favourite state of a module when the user clicks on the \"starred\" button.\n *\n * @param {HTMLElement} target The target element that triggered the event\n * @return {Promise} A promise that resolves when the event is handled\n */\n async handleFavouriteClick(target) {\n const caller = target.closest(selectors.actions.optionActions.manageFavourite);\n const id = caller.dataset.id;\n const name = caller.dataset.name;\n const internal = caller.dataset.internal;\n const isFavourite = caller.dataset.favourited;\n\n // Switch on fave or not.\n if (isFavourite === 'true') {\n await Repository.unfavouriteModule(name, id);\n this.updateFavouriteItemValue(internal, false);\n } else {\n await Repository.favouriteModule(name, id);\n this.updateFavouriteItemValue(internal, true);\n }\n }\n\n /**\n * Handle a clear search action.\n */\n handleClearSearch() {\n const searchInput = this.dialogueDom.getSearchInputElement();\n searchInput.value = \"\";\n searchInput.focus();\n this.toggleSearchResultsView(searchInput.value);\n }\n\n /**\n * Handle the click on a chooser option.\n *\n * @param {HTMLElement} target The target element that triggered the event\n */\n handleOptionSelection(target) {\n const option = this.dialogueDom.getClosestChooserOption(target);\n if (option === null) {\n return;\n }\n this.selectedModule = option;\n this.dialogueDom.markChooserOptionAsSelected(option, getFirst(this.modal.getFooter()));\n }\n\n /**\n * Submit the selected module to the chooser.\n *\n * This method will redirect the user to the URL of the selected module, if one is selected.\n *\n * @param {HTMLElement} newSelectedModule optional new selected module element.\n * @return {void}\n */\n submitAddSelectedModule(newSelectedModule = null) {\n if (newSelectedModule) {\n this.handleOptionSelection(newSelectedModule);\n }\n if (this.selectedModule === null) {\n return;\n }\n window.location.href = this.dialogueDom.getChooserOptionUrl(this.selectedModule);\n }\n\n /**\n * Set up our tabindex information across the chooser.\n *\n * @method setupKeyboardAccessibility\n */\n setupKeyboardAccessibility() {\n const mainElement = getFirst(this.modal.getModal());\n\n mainElement.tabIndex = -1;\n\n mainElement.addEventListener('keydown', (e) => {\n const currentOption = this.dialogueDom.getClosestChooserOption(e.target);\n if (currentOption === null) {\n return;\n }\n\n if (e.keyCode === enter || e.keyCode === space) {\n // Check first if the target is an internal control button (favourite or help).\n // If that is the case, the regular click will handle the event.\n if (e.target.closest(selectors.regions.chooserOption.actions)) {\n return;\n }\n }\n\n if (e.keyCode === space) {\n e.preventDefault();\n this.handleOptionSelection(currentOption);\n }\n if (e.keyCode === enter) {\n e.preventDefault();\n this.submitAddSelectedModule(currentOption);\n }\n if (e.keyCode === arrowRight) {\n e.preventDefault();\n this.dialogueDom.focusNextChooserOption(currentOption);\n }\n if (e.keyCode === arrowLeft) {\n e.preventDefault();\n this.dialogueDom.focusPreviousChooserOption(currentOption);\n }\n if (e.keyCode === home) {\n e.preventDefault();\n this.dialogueDom.focusFirstChooserOption(currentOption);\n }\n if (e.keyCode === end) {\n e.preventDefault();\n this.dialogueDom.focusLastChooserOption(currentOption);\n }\n });\n }\n\n /**\n * Toggle (display/hide) the search results depending on the value of the search query\n *\n * @method toggleSearchResultsView\n * @param {String} searchQuery The search query\n */\n async toggleSearchResultsView(searchQuery) {\n const searchResultsData = this.searchModules(searchQuery);\n\n if (searchQuery.length > 0) {\n await this.dialogueDom.refreshSearchResults(searchResultsData);\n this.dialogueDom.showAllActivitiesTab(true);\n } else {\n this.dialogueDom.cleanSearchResults();\n }\n }\n\n /**\n * Return the list of modules which have a name or description that matches the given search term.\n *\n * @method searchModules\n * @param {String} searchTerm The search term to match\n * @return {Array}\n */\n searchModules(searchTerm) {\n if (searchTerm === '') {\n return this.mappedModules;\n }\n searchTerm = searchTerm.toLowerCase();\n const searchResults = [];\n this.mappedModules.forEach((activity) => {\n const activityName = activity.title.toLowerCase();\n const activityDesc = activity.help.toLowerCase();\n if (activityName.includes(searchTerm) || activityDesc.includes(searchTerm)) {\n searchResults.push(activity);\n }\n });\n\n return searchResults;\n }\n\n /**\n * Update the favourite item value in the mapped modules.\n *\n * @param {String} internal The internal name of the module.\n * @param {Boolean} favourite Whether the module is a favourite or not.\n * @return {Promise} A promise that resolves when the item is updated.\n */\n async updateFavouriteItemValue(internal, favourite) {\n const moduleItem = this.mappedModules.find(({name}) => name === internal);\n if (!moduleItem) {\n return;\n }\n moduleItem.favourite = favourite;\n\n this.dialogueDom.updateItemStarredIcons(internal, favourite);\n\n if (this.dialogueDom.isFavoutiteTabActive()) {\n this.isFavouriteTabDirty = true;\n } else {\n this.refreshFavouritesTabContent();\n }\n }\n\n /**\n * Refresh the favourites tab content.\n *\n * Note: this method will also hide the favourites tab if there are no favourite modules\n * to keep the modal consistent.\n *\n * @return {Promise} A promise that resolves when the content is refreshed.\n */\n async refreshFavouritesTabContent() {\n this.isFavouriteTabDirty = false;\n const favouriteCount = this.mappedModules.filter(mod => mod.favourite === true).size;\n this.dialogueDom.toggleFavouriteTabDisplay(favouriteCount > 0);\n await this.dialogueDom.refreshFavouritesTabContent(this.mappedModules);\n }\n}\n"],"names":["footerDataPromise","modulesDataPromise","bodyPromiseResolver","bodyPromise","Promise","resolve","exporter","Exporter","footerData","footerPromise","Templates","render","getFooterData","sectionModal","Modal","create","body","title","footer","large","scrollable","templateContext","classes","show","modulesData","modal","ActivityChooserDialogue","templateData","getModChooserTemplateData","error","errorTemplateData","message","modalPromise","sectionModules","partialFavourite","window","console","warn","then","catch","Notification","exception","constructor","dialogueDom","selectedModule","isFavouriteTabDirty","mappedModules","Map","forEach","module","set","componentname","link","init","this","getBodyPromise","getFooterPromise","DialogueDom","registerModalListenerEvents","setupKeyboardAccessibility","getRoot","on","ModalEvents","hidden","destroy","unmarkAllChooserOptionAsSelected","modalRoot","addEventListener","event","target","closest","selectors","regions","allTabNav","searchInput","getSearchInputElement","value","length","toggleSearchResultsView","handleModalClick","bind","handleModalDoubleClick","pending","initBootstrapComponents","relatedTarget","disableFocusAllChooserOptions","initActiveTabNavigation","isFavoutiteTabActive","refreshFavouritesTabContent","handleFooterClick","undefined","to","toggleActiveFooter","toggleBackButton","toggleAddButton","actions","addSelectedChooserOption","submitAddSelectedModule","activeFooter","footerjs","pluginName","customfooterjs","footerClickListener","preventDefault","optionActions","showSummary","handleShowSummary","manageFavourite","handleFavouriteClick","matches","closeOption","hideModuleHelp","dataset","modname","clearSearch","chooserOption","info","handleOptionSelection","handleClearSearch","getClosestChooserOption","moduleName","moduleData","get","showFooter","hasFooterContent","setBackButtonModuleData","showModuleHelp","caller","id","name","internal","favourited","Repository","unfavouriteModule","updateFavouriteItemValue","favouriteModule","focus","option","markChooserOptionAsSelected","getFooter","newSelectedModule","location","href","getChooserOptionUrl","mainElement","getModal","tabIndex","e","currentOption","keyCode","enter","space","arrowRight","focusNextChooserOption","arrowLeft","focusPreviousChooserOption","home","focusFirstChooserOption","end","focusLastChooserOption","searchQuery","searchResultsData","searchModules","refreshSearchResults","showAllActivitiesTab","cleanSearchResults","searchTerm","toLowerCase","searchResults","activity","activityName","activityDesc","help","includes","push","favourite","moduleItem","find","_ref","updateItemStarredIcons","favouriteCount","filter","mod","size","toggleFavouriteTabDisplay"],"mappings":"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"} \ No newline at end of file diff --git a/public/course/amd/build/local/activitychooser/dialoguedom.min.js b/public/course/amd/build/local/activitychooser/dialoguedom.min.js index d113c46884c..da5f47febdd 100644 --- a/public/course/amd/build/local/activitychooser/dialoguedom.min.js +++ b/public/course/amd/build/local/activitychooser/dialoguedom.min.js @@ -5,6 +5,6 @@ define("core_course/local/activitychooser/dialoguedom",["exports","core/loadingi * @module core_course/local/activitychooser/dialoguedom * @copyright 2025 Ferran Recio * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_carousel=_interopRequireDefault(_carousel),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),_selectors=_interopRequireDefault(_selectors),_tab=_interopRequireDefault(_tab),Templates=function(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]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Templates);return _exports.default=class{constructor(dialogue,modal,exporter){this.modal=modal,this.modalBody=(0,_normalise.getFirst)(this.modal.getBody()),this.exporter=exporter,this.dialogue=dialogue}getSearchInputElement(){return this.modalBody.querySelector(_selectors.default.actions.search)}getClosestChooserOption(element){return element.closest(_selectors.default.regions.chooserOption.container)}isFavoutiteTabActive(){const favouriteTab=this.modalBody.querySelector(_selectors.default.regions.favouriteTabNav);return favouriteTab&&favouriteTab.classList.contains("active")}getChooserOptionUrl(optionContainer){const optionLink=optionContainer.querySelector(_selectors.default.actions.addChooser);if(!optionLink)throw new Error("Invalid chooser option container: no link found");return optionLink.getAttribute("href")}async refreshSearchResults(searchResultsData){const searchResultsContainer=this.modalBody.querySelector(_selectors.default.regions.searchResults),clearSearchButton=this.modalBody.querySelector(_selectors.default.actions.clearSearch);await this.renderSearchResults(searchResultsContainer,searchResultsData);const firstSearchResultItem=searchResultsContainer.querySelector(_selectors.default.regions.chooserOptions).querySelector(_selectors.default.regions.chooserOption.container);firstSearchResultItem&&this.toggleFocusableChooserOption(firstSearchResultItem,!0),clearSearchButton.classList.remove("d-none");searchResultsContainer.closest(_selectors.default.regions.tabContent).querySelector(_selectors.default.regions.categoryContent).classList.add("d-none")}cleanSearchResults(){const searchResultsContainer=this.modalBody.querySelector(_selectors.default.regions.searchResults),clearSearchButton=this.modalBody.querySelector(_selectors.default.actions.clearSearch);searchResultsContainer.innerHTML="",clearSearchButton.classList.add("d-none");searchResultsContainer.closest(_selectors.default.regions.tabContent).querySelector(_selectors.default.regions.categoryContent).classList.remove("d-none")}async renderSearchResults(searchResultsContainer,searchResultsData){const templateData=this.exporter.getSearchResultData(searchResultsData),{html:html,js:js}=await Templates.renderForPromise("core_course/local/activitychooser/search_results",templateData);await Templates.replaceNodeContents(searchResultsContainer,html,js)}showAllActivitiesTab(){const navTab=this.modalBody.querySelector(_selectors.default.regions.allTabNav);if(navTab.classList.contains("active"))return navTab;const pendingPromise=new _pending.default("core_course/activitychooser:alltab");return navTab.addEventListener("shown.bs.tab",pendingPromise.resolve,{once:!0}),_tab.default.getOrCreateInstance(navTab).show(),navTab}updateItemStarredIcons(internal,favourite){const favouriteButtons=this.modalBody.querySelectorAll("".concat(_selectors.default.elements.moduleItem(internal)," ").concat(_selectors.default.actions.optionActions.manageFavourite));Array.from(favouriteButtons).forEach((element=>{var _element$querySelecto,_element$querySelecto2;element.classList.toggle("text-muted",!favourite),element.classList.toggle("text-primary",favourite),element.dataset.favourited=favourite,element.setAttribute("aria-pressed",favourite),null===(_element$querySelecto=element.querySelector(_selectors.default.elements.favouriteIconActive))||void 0===_element$querySelecto||_element$querySelecto.classList.toggle("d-none",!favourite),null===(_element$querySelecto2=element.querySelector(_selectors.default.elements.favouriteIconInactive))||void 0===_element$querySelecto2||_element$querySelecto2.classList.toggle("d-none",favourite);const iconSelectsor=favourite?_selectors.default.elements.favouriteIconActive:_selectors.default.elements.favouriteIconInactive,favouriteIcon=element.querySelector(iconSelectsor);element.setAttribute("aria-label",(null==favouriteIcon?void 0:favouriteIcon.getAttribute("data-action-label"))||"")}))}async refreshFavouritesTabContent(mappedModules){const templateData=await this.exporter.getFavouriteTabData(mappedModules),favouriteArea=this.modalBody.querySelector(_selectors.default.regions.favouriteTab),{html:html,js:js}=await Templates.renderForPromise("core_course/local/activitychooser/tabcontent",templateData);await Templates.replaceNodeContents(favouriteArea,html,js)}toggleFavouriteTabDisplay(displayed){const favouriteTabNav=this.modalBody.querySelector(_selectors.default.regions.favouriteTabNav);let moveFocusTo;!displayed&&favouriteTabNav.classList.contains("active")&&(moveFocusTo=this.showAllActivitiesTab()),null==favouriteTabNav||favouriteTabNav.classList.toggle("d-none",!displayed),favouriteTabNav.tabIndex=displayed?0:-1,displayed?favouriteTabNav.classList.remove("disabled"):favouriteTabNav.classList.add("disabled"),moveFocusTo&&moveFocusTo.focus(),this.initActiveTabNavigation()}async showModuleHelp(moduleData){const carousel=this.modalBody.querySelector(_selectors.default.regions.carousel),help=carousel.querySelector(_selectors.default.regions.help);help.innerHTML="",help.classList.add("m-auto");const spinnerPromise=(0,_loadingicon.addIconToContainer)(help);let transitionPromiseResolver=null;const transitionPromise=new Promise((resolve=>{transitionPromiseResolver=resolve})),contentPromise=Templates.renderForPromise("core_course/local/activitychooser/help",await this.exporter.getModuleHelpTemplateData(moduleData));Promise.all([contentPromise,spinnerPromise,transitionPromise]).then((_ref=>{let[{html:html,js:js}]=_ref;return Templates.replaceNodeContents(help,html,js)})).then((()=>(help.querySelector(_selectors.default.regions.chooserSummary.header).focus(),help))).catch(_notification.default.exception),carousel.addEventListener("slid.bs.carousel",(()=>{transitionPromiseResolver()}),{once:!0}),_carousel.default.getInstance(carousel).next()}hideModuleHelp(){let internal=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;const carousel=this.modalBody.querySelector(_selectors.default.regions.carousel);_carousel.default.getInstance(carousel).to(0),internal&&carousel.addEventListener("slid.bs.carousel",(()=>{this.focusChooserOption(internal)}),{once:!0})}setBackButtonModuleData(moduleData){const footer=(0,_normalise.getFirst)(this.modal.getFooter()),modnameValue="".concat(moduleData.componentname,"_").concat(moduleData.link);footer.querySelector(_selectors.default.actions.closeOption).dataset.modname=modnameValue}toggleBackButton(show){(0,_normalise.getFirst)(this.modal.getFooter()).querySelector(_selectors.default.actions.closeOption).classList.toggle("d-none",!show)}toggleAddButton(show){(0,_normalise.getFirst)(this.modal.getFooter()).querySelector(_selectors.default.actions.addSelectedChooserOption).classList.toggle("d-none",!show)}toggleActiveFooter(show){(0,_normalise.getFirst)(this.modal.getFooter()).querySelector(_selectors.default.regions.activeFooter).classList.toggle("d-none",!show)}focusChooserOption(internal){const currentTabNav=this.modalBody.querySelector(_selectors.default.elements.activetab),activeSectionId=currentTabNav.getAttribute("href"),newCurrent=this.modalBody.querySelector(_selectors.default.regions.getSectionChooserOptions(activeSectionId)).querySelector(_selectors.default.regions.getModuleSelector(internal));if(!newCurrent)throw new Error("Invalid chooser option to focus on: ".concat(internal));this.disableFocusAllChooserOptions(currentTabNav),this.toggleFocusableChooserOption(newCurrent,!0),newCurrent.contentEditable=!0,newCurrent.focus(),newCurrent.contentEditable=!1}initActiveTabNavigation(){const activeSectionId=this.modalBody.querySelector(_selectors.default.elements.activetab).getAttribute("href"),sectionChooserOptions=this.modalBody.querySelector(_selectors.default.regions.getSectionChooserOptions(activeSectionId)),firstChooserOption=null==sectionChooserOptions?void 0:sectionChooserOptions.querySelector(_selectors.default.regions.chooserOption.container);firstChooserOption&&this.toggleFocusableChooserOption(firstChooserOption,!0)}initBootstrapComponents(){this.modalBody.querySelectorAll(_selectors.default.elements.tab).forEach((navTab=>{_tab.default.getOrCreateInstance(navTab)}));const carousel=this.modalBody.querySelector(_selectors.default.regions.carousel);new _carousel.default(carousel,{interval:!1,pause:!0,keyboard:!1})}disableFocusAllChooserOptions(tabNav){const tabId=tabNav.getAttribute("href"),chooserOptions=this.modalBody.querySelector(_selectors.default.regions.getSectionChooserOptions(tabId));if(null===chooserOptions)return;chooserOptions.querySelectorAll(_selectors.default.regions.chooserOption.container).forEach((chooserOption=>{this.toggleFocusableChooserOption(chooserOption,!1)}))}toggleFocusableChooserOption(chooserOption,isFocusable){const chooserOptionLink=chooserOption.querySelector(_selectors.default.actions.addChooser),chooserOptionHelp=chooserOption.querySelector(_selectors.default.actions.optionActions.showSummary),chooserOptionFavourite=chooserOption.querySelector(_selectors.default.actions.optionActions.manageFavourite);isFocusable?(chooserOption.tabIndex=0,chooserOptionLink.tabIndex=0,chooserOptionHelp.tabIndex=0,chooserOptionFavourite.tabIndex=0):(chooserOption.tabIndex=-1,chooserOptionLink.tabIndex=-1,chooserOptionHelp.tabIndex=-1,chooserOptionFavourite.tabIndex=-1)}focusNextChooserOption(current){this.moveChooserOptionFocus(current,(currentOption=>{var _currentOption$nextEl;return null!==(_currentOption$nextEl=currentOption.nextElementSibling)&&void 0!==_currentOption$nextEl?_currentOption$nextEl:currentOption}))}focusPreviousChooserOption(current){this.moveChooserOptionFocus(current,(currentOption=>{var _currentOption$previo;return null!==(_currentOption$previo=currentOption.previousElementSibling)&&void 0!==_currentOption$previo?_currentOption$previo:currentOption}))}focusFirstChooserOption(current){this.moveChooserOptionFocus(current,((currentOption,container)=>{var _container$firstEleme;return null!==(_container$firstEleme=container.firstElementChild)&&void 0!==_container$firstEleme?_container$firstEleme:currentOption}))}focusLastChooserOption(current){this.moveChooserOptionFocus(current,((currentOption,container)=>{var _container$lastElemen;return null!==(_container$lastElemen=container.lastElementChild)&&void 0!==_container$lastElemen?_container$lastElemen:currentOption}))}moveChooserOptionFocus(current,getNextFocus){const currentOption=this.getClosestChooserOption(current),container=current.closest(_selectors.default.regions.chooserOptions);if(!container||!currentOption)throw new Error("Invalid chooser options container or current option");const newFocusOption=getNextFocus(currentOption,container);newFocusOption&&(this.toggleFocusableChooserOption(currentOption,!1),this.toggleFocusableChooserOption(newFocusOption,!0),newFocusOption.focus())}markChooserOptionAsSelected(chooserOption){this.unmarkAllChooserOptionAsSelected(),chooserOption.classList.add("selected"),chooserOption.classList.add("border-primary"),chooserOption.setAttribute("aria-selected","true");const addButton=(0,_normalise.getFirst)(this.modal.getFooter()).querySelector(_selectors.default.actions.addSelectedChooserOption);addButton&&addButton.removeAttribute("disabled")}unmarkAllChooserOptionAsSelected(){this.modalBody.querySelectorAll("".concat(_selectors.default.regions.chooserOption.container,".selected")).forEach((option=>{option.classList.remove("selected"),option.classList.remove("border-primary"),option.setAttribute("aria-selected","false")}));const addButton=(0,_normalise.getFirst)(this.modal.getFooter()).querySelector(_selectors.default.actions.addSelectedChooserOption);addButton&&addButton.setAttribute("disabled","disabled")}},_exports.default})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_carousel=_interopRequireDefault(_carousel),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),_selectors=_interopRequireDefault(_selectors),_tab=_interopRequireDefault(_tab),Templates=function(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]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Templates);return _exports.default=class{constructor(dialogue,modal,exporter){this.modal=modal,this.modalBody=(0,_normalise.getFirst)(this.modal.getBody()),this.exporter=exporter,this.dialogue=dialogue}getSearchInputElement(){return this.modalBody.querySelector(_selectors.default.actions.search)}getClosestChooserOption(element){return element.closest(_selectors.default.regions.chooserOption.container)}isFavoutiteTabActive(){const favouriteTab=this.modalBody.querySelector(_selectors.default.regions.favouriteTabNav);return favouriteTab&&favouriteTab.classList.contains("active")}getChooserOptionUrl(optionContainer){const optionLink=optionContainer.querySelector(_selectors.default.actions.addChooser);if(!optionLink)throw new Error("Invalid chooser option container: no link found");return optionLink.getAttribute("href")}async refreshSearchResults(searchResultsData){const searchResultsContainer=this.modalBody.querySelector(_selectors.default.regions.searchResults),clearSearchButton=this.modalBody.querySelector(_selectors.default.actions.clearSearch);await this.renderSearchResults(searchResultsContainer,searchResultsData);const firstSearchResultItem=searchResultsContainer.querySelector(_selectors.default.regions.chooserOptions).querySelector(_selectors.default.regions.chooserOption.container);firstSearchResultItem&&this.toggleFocusableChooserOption(firstSearchResultItem,!0),clearSearchButton.classList.remove("d-none");searchResultsContainer.closest(_selectors.default.regions.tabContent).querySelector(_selectors.default.regions.categoryContent).classList.add("d-none")}cleanSearchResults(){const searchResultsContainer=this.modalBody.querySelector(_selectors.default.regions.searchResults),clearSearchButton=this.modalBody.querySelector(_selectors.default.actions.clearSearch);searchResultsContainer.innerHTML="",clearSearchButton.classList.add("d-none");searchResultsContainer.closest(_selectors.default.regions.tabContent).querySelector(_selectors.default.regions.categoryContent).classList.remove("d-none")}async renderSearchResults(searchResultsContainer,searchResultsData){const templateData=this.exporter.getSearchResultData(searchResultsData),{html:html,js:js}=await Templates.renderForPromise("core_course/local/activitychooser/search_results",templateData);await Templates.replaceNodeContents(searchResultsContainer,html,js)}showAllActivitiesTab(){const navTab=this.modalBody.querySelector(_selectors.default.regions.allTabNav);if(navTab.classList.contains("active"))return navTab;const pendingPromise=new _pending.default("core_course/activitychooser:alltab");return navTab.addEventListener("shown.bs.tab",pendingPromise.resolve,{once:!0}),_tab.default.getOrCreateInstance(navTab).show(),navTab}updateItemStarredIcons(internal,favourite){const favouriteButtons=this.modalBody.querySelectorAll("".concat(_selectors.default.elements.moduleItem(internal)," ").concat(_selectors.default.actions.optionActions.manageFavourite));Array.from(favouriteButtons).forEach((element=>{var _element$querySelecto,_element$querySelecto2;element.dataset.favourited=favourite,element.setAttribute("aria-pressed",favourite),null===(_element$querySelecto=element.querySelector(_selectors.default.elements.favouriteIconActive))||void 0===_element$querySelecto||_element$querySelecto.classList.toggle("d-none",!favourite),null===(_element$querySelecto2=element.querySelector(_selectors.default.elements.favouriteIconInactive))||void 0===_element$querySelecto2||_element$querySelecto2.classList.toggle("d-none",favourite);const iconSelectsor=favourite?_selectors.default.elements.favouriteIconActive:_selectors.default.elements.favouriteIconInactive,favouriteIcon=element.querySelector(iconSelectsor);element.setAttribute("aria-label",(null==favouriteIcon?void 0:favouriteIcon.getAttribute("data-action-label"))||""),element.setAttribute("title",(null==favouriteIcon?void 0:favouriteIcon.getAttribute("data-action-label"))||"")}))}async refreshFavouritesTabContent(mappedModules){const templateData=await this.exporter.getFavouriteTabData(mappedModules),favouriteArea=this.modalBody.querySelector(_selectors.default.regions.favouriteTab),{html:html,js:js}=await Templates.renderForPromise("core_course/local/activitychooser/tabcontent",templateData);await Templates.replaceNodeContents(favouriteArea,html,js)}toggleFavouriteTabDisplay(displayed){const favouriteTabNav=this.modalBody.querySelector(_selectors.default.regions.favouriteTabNav);let moveFocusTo;!displayed&&favouriteTabNav.classList.contains("active")&&(moveFocusTo=this.showAllActivitiesTab()),null==favouriteTabNav||favouriteTabNav.classList.toggle("d-none",!displayed),favouriteTabNav.tabIndex=displayed?0:-1,displayed?favouriteTabNav.classList.remove("disabled"):favouriteTabNav.classList.add("disabled"),moveFocusTo&&moveFocusTo.focus(),this.initActiveTabNavigation()}async showModuleHelp(moduleData){const carousel=this.modalBody.querySelector(_selectors.default.regions.carousel),help=carousel.querySelector(_selectors.default.regions.help);help.innerHTML="",help.classList.add("m-auto");const spinnerPromise=(0,_loadingicon.addIconToContainer)(help);let transitionPromiseResolver=null;const transitionPromise=new Promise((resolve=>{transitionPromiseResolver=resolve})),contentPromise=Templates.renderForPromise("core_course/local/activitychooser/help",await this.exporter.getModuleHelpTemplateData(moduleData));Promise.all([contentPromise,spinnerPromise,transitionPromise]).then((_ref=>{let[{html:html,js:js}]=_ref;return Templates.replaceNodeContents(help,html,js)})).then((()=>(help.querySelector(_selectors.default.regions.chooserSummary.header).focus(),help))).catch(_notification.default.exception),carousel.addEventListener("slid.bs.carousel",(()=>{transitionPromiseResolver()}),{once:!0}),_carousel.default.getInstance(carousel).next()}hideModuleHelp(){let internal=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;const carousel=this.modalBody.querySelector(_selectors.default.regions.carousel);_carousel.default.getInstance(carousel).to(0),internal&&carousel.addEventListener("slid.bs.carousel",(()=>{this.focusChooserOption(internal)}),{once:!0})}setBackButtonModuleData(moduleData){const footer=(0,_normalise.getFirst)(this.modal.getFooter()),modnameValue="".concat(moduleData.componentname,"_").concat(moduleData.link);footer.querySelector(_selectors.default.actions.closeOption).dataset.modname=modnameValue}toggleBackButton(show){(0,_normalise.getFirst)(this.modal.getFooter()).querySelector(_selectors.default.actions.closeOption).classList.toggle("d-none",!show)}toggleAddButton(show){(0,_normalise.getFirst)(this.modal.getFooter()).querySelector(_selectors.default.actions.addSelectedChooserOption).classList.toggle("d-none",!show)}toggleActiveFooter(show){(0,_normalise.getFirst)(this.modal.getFooter()).querySelector(_selectors.default.regions.activeFooter).classList.toggle("d-none",!show)}focusChooserOption(internal){const currentTabNav=this.modalBody.querySelector(_selectors.default.elements.activetab),activeSectionId=currentTabNav.getAttribute("href"),newCurrent=this.modalBody.querySelector(_selectors.default.regions.getSectionChooserOptions(activeSectionId)).querySelector(_selectors.default.regions.getModuleSelector(internal));if(!newCurrent)throw new Error("Invalid chooser option to focus on: ".concat(internal));this.disableFocusAllChooserOptions(currentTabNav),this.toggleFocusableChooserOption(newCurrent,!0),newCurrent.contentEditable=!0,newCurrent.focus(),newCurrent.contentEditable=!1}initActiveTabNavigation(){const activeSectionId=this.modalBody.querySelector(_selectors.default.elements.activetab).getAttribute("href"),sectionChooserOptions=this.modalBody.querySelector(_selectors.default.regions.getSectionChooserOptions(activeSectionId)),firstChooserOption=null==sectionChooserOptions?void 0:sectionChooserOptions.querySelector(_selectors.default.regions.chooserOption.container);firstChooserOption&&this.toggleFocusableChooserOption(firstChooserOption,!0)}initBootstrapComponents(){this.modalBody.querySelectorAll(_selectors.default.elements.tab).forEach((navTab=>{_tab.default.getOrCreateInstance(navTab)}));const carousel=this.modalBody.querySelector(_selectors.default.regions.carousel);new _carousel.default(carousel,{interval:!1,pause:!0,keyboard:!1})}disableFocusAllChooserOptions(tabNav){const tabId=tabNav.getAttribute("href"),chooserOptions=this.modalBody.querySelector(_selectors.default.regions.getSectionChooserOptions(tabId));if(null===chooserOptions)return;chooserOptions.querySelectorAll(_selectors.default.regions.chooserOption.container).forEach((chooserOption=>{this.toggleFocusableChooserOption(chooserOption,!1)}))}toggleFocusableChooserOption(chooserOption,isFocusable){const chooserOptionLink=chooserOption.querySelector(_selectors.default.actions.addChooser),chooserOptionHelp=chooserOption.querySelector(_selectors.default.actions.optionActions.showSummary),chooserOptionFavourite=chooserOption.querySelector(_selectors.default.actions.optionActions.manageFavourite);isFocusable?(chooserOption.tabIndex=0,chooserOptionLink.tabIndex=0,chooserOptionHelp.tabIndex=0,chooserOptionFavourite.tabIndex=0):(chooserOption.tabIndex=-1,chooserOptionLink.tabIndex=-1,chooserOptionHelp.tabIndex=-1,chooserOptionFavourite.tabIndex=-1)}focusNextChooserOption(current){this.moveChooserOptionFocus(current,(currentOption=>{var _currentOption$nextEl;return null!==(_currentOption$nextEl=currentOption.nextElementSibling)&&void 0!==_currentOption$nextEl?_currentOption$nextEl:currentOption}))}focusPreviousChooserOption(current){this.moveChooserOptionFocus(current,(currentOption=>{var _currentOption$previo;return null!==(_currentOption$previo=currentOption.previousElementSibling)&&void 0!==_currentOption$previo?_currentOption$previo:currentOption}))}focusFirstChooserOption(current){this.moveChooserOptionFocus(current,((currentOption,container)=>{var _container$firstEleme;return null!==(_container$firstEleme=container.firstElementChild)&&void 0!==_container$firstEleme?_container$firstEleme:currentOption}))}focusLastChooserOption(current){this.moveChooserOptionFocus(current,((currentOption,container)=>{var _container$lastElemen;return null!==(_container$lastElemen=container.lastElementChild)&&void 0!==_container$lastElemen?_container$lastElemen:currentOption}))}moveChooserOptionFocus(current,getNextFocus){const currentOption=this.getClosestChooserOption(current),container=current.closest(_selectors.default.regions.chooserOptions);if(!container||!currentOption)throw new Error("Invalid chooser options container or current option");const newFocusOption=getNextFocus(currentOption,container);newFocusOption&&(this.toggleFocusableChooserOption(currentOption,!1),this.toggleFocusableChooserOption(newFocusOption,!0),newFocusOption.focus())}markChooserOptionAsSelected(chooserOption){this.unmarkAllChooserOptionAsSelected(),chooserOption.classList.add("selected"),chooserOption.setAttribute("aria-selected","true");const addButton=(0,_normalise.getFirst)(this.modal.getFooter()).querySelector(_selectors.default.actions.addSelectedChooserOption);addButton&&addButton.removeAttribute("disabled")}unmarkAllChooserOptionAsSelected(){this.modalBody.querySelectorAll("".concat(_selectors.default.regions.chooserOption.container,".selected")).forEach((option=>{option.classList.remove("selected"),option.setAttribute("aria-selected","false")}));const addButton=(0,_normalise.getFirst)(this.modal.getFooter()).querySelector(_selectors.default.actions.addSelectedChooserOption);addButton&&addButton.setAttribute("disabled","disabled")}},_exports.default})); //# sourceMappingURL=dialoguedom.min.js.map \ No newline at end of file diff --git a/public/course/amd/build/local/activitychooser/dialoguedom.min.js.map b/public/course/amd/build/local/activitychooser/dialoguedom.min.js.map index 4a3a9f98a09..dbd689acdec 100644 --- a/public/course/amd/build/local/activitychooser/dialoguedom.min.js.map +++ b/public/course/amd/build/local/activitychooser/dialoguedom.min.js.map @@ -1 +1 @@ -{"version":3,"file":"dialoguedom.min.js","sources":["../../../src/local/activitychooser/dialoguedom.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\nimport {addIconToContainer} from 'core/loadingicon';\nimport Carousel from 'theme_boost/bootstrap/carousel';\nimport {getFirst} from 'core/normalise';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport selectors from 'core_course/local/activitychooser/selectors';\nimport Tab from 'theme_boost/bootstrap/tab';\nimport * as Templates from 'core/templates';\n\n\n/**\n * The activity changer dialogue DOM manipulation module.\n *\n * @module core_course/local/activitychooser/dialoguedom\n * @copyright 2025 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nexport default class ChooserDialogueDOM {\n /**\n * ChooserDialogueDOM constructor.\n *\n * @param {Object} dialogue The dialogue object.\n * @param {Modal} modal The modal object.\n * @param {Object} exporter The exporter object to generate template data.\n */\n constructor(dialogue, modal, exporter) {\n this.modal = modal;\n this.modalBody = getFirst(this.modal.getBody());\n this.exporter = exporter;\n // Temporal variable while migrating methods.\n this.dialogue = dialogue;\n }\n\n /**\n * Get the search input element.\n *\n * @return {HTMLElement} The search input element.\n */\n getSearchInputElement() {\n return this.modalBody.querySelector(selectors.actions.search);\n }\n\n /**\n * Get the closest chooser option element.\n *\n * @param {HTMLElement} element\n * @return {HTMLElement|null} element\n */\n getClosestChooserOption(element) {\n return element.closest(selectors.regions.chooserOption.container);\n }\n\n /**\n * Check if the search tab is active.\n *\n * @return {Boolean} True if the search tab is active, false otherwise.\n */\n isFavoutiteTabActive() {\n const favouriteTab = this.modalBody.querySelector(selectors.regions.favouriteTabNav);\n return favouriteTab && favouriteTab.classList.contains('active');\n }\n\n /**\n * Get the URL of a chooser option.\n *\n * @param {HTMLElement} optionContainer The container of the chooser option.\n * @return {String} The URL of the chooser option.\n * @throws {Error} If the option container does not contain a link.\n */\n getChooserOptionUrl(optionContainer) {\n const optionLink = optionContainer.querySelector(selectors.actions.addChooser);\n if (!optionLink) {\n throw new Error('Invalid chooser option container: no link found');\n }\n return optionLink.getAttribute('href');\n }\n\n /**\n * Show the search results.\n *\n * @param {Object} searchResultsData Data containing the module items that satisfy the search criteria\n */\n async refreshSearchResults(searchResultsData) {\n const searchResultsContainer = this.modalBody.querySelector(selectors.regions.searchResults);\n const clearSearchButton = this.modalBody.querySelector(selectors.actions.clearSearch);\n\n await this.renderSearchResults(searchResultsContainer, searchResultsData);\n const chooserOptionsContainer = searchResultsContainer.querySelector(selectors.regions.chooserOptions);\n const firstSearchResultItem = chooserOptionsContainer.querySelector(selectors.regions.chooserOption.container);\n if (firstSearchResultItem) {\n // Set the first result item to be focusable.\n this.toggleFocusableChooserOption(firstSearchResultItem, true);\n // Register keyboard events on the created search result items.\n }\n clearSearchButton.classList.remove('d-none');\n\n // Results are rendered in the all activities tab, so we need to hide the category content.\n const tabContent = searchResultsContainer.closest(selectors.regions.tabContent);\n const categoryContent = tabContent.querySelector(selectors.regions.categoryContent);\n categoryContent.classList.add('d-none');\n }\n\n /**\n * Clear the search results.\n */\n cleanSearchResults() {\n const searchResultsContainer = this.modalBody.querySelector(selectors.regions.searchResults);\n const clearSearchButton = this.modalBody.querySelector(selectors.actions.clearSearch);\n searchResultsContainer.innerHTML = '';\n clearSearchButton.classList.add('d-none');\n\n // Results are rendered in the all activities tab, so we need to show the category content again.\n const tabContent = searchResultsContainer.closest(selectors.regions.tabContent);\n const categoryContent = tabContent.querySelector(selectors.regions.categoryContent);\n categoryContent.classList.remove('d-none');\n }\n\n /**\n * Render the search results in a defined container\n *\n * @private\n * @method renderSearchResults\n * @param {HTMLElement} searchResultsContainer The container where the data should be rendered\n * @param {Object} searchResultsData Data containing the module items that satisfy the search criteria\n */\n async renderSearchResults(searchResultsContainer, searchResultsData) {\n const templateData = this.exporter.getSearchResultData(searchResultsData);\n // Build up the html & js ready to place into the help section.\n const {html, js} = await Templates.renderForPromise(\n 'core_course/local/activitychooser/search_results',\n templateData\n );\n await Templates.replaceNodeContents(searchResultsContainer, html, js);\n }\n\n /**\n * Show the \"All activities\" tab.\n *\n * @method showAllActivitiesTab\n * @return {HTMLElement} The \"All activities\" tab element.\n */\n showAllActivitiesTab() {\n const navTab = this.modalBody.querySelector(selectors.regions.allTabNav);\n\n if (navTab.classList.contains('active')) {\n return navTab;\n }\n\n const pendingPromise = new Pending('core_course/activitychooser:alltab');\n navTab.addEventListener('shown.bs.tab', pendingPromise.resolve, {once: true});\n\n Tab.getOrCreateInstance(navTab).show();\n return navTab;\n }\n\n /**\n * Update the starred icons in the chooser modal.\n *\n * @method updateItemStarredIcons\n * @param {String} internal The internal name of the module.\n * @param {Boolean} favourite Whether the module is a favourite or not.\n */\n updateItemStarredIcons(internal, favourite) {\n const favouriteButtons = this.modalBody.querySelectorAll(\n `${selectors.elements.moduleItem(internal)} ${selectors.actions.optionActions.manageFavourite}`\n );\n Array.from(favouriteButtons).forEach((element) => {\n element.classList.toggle('text-muted', !favourite);\n element.classList.toggle('text-primary', favourite);\n element.dataset.favourited = favourite;\n element.setAttribute('aria-pressed', favourite);\n element.querySelector(selectors.elements.favouriteIconActive)?.classList.toggle('d-none', !favourite);\n element.querySelector(selectors.elements.favouriteIconInactive)?.classList.toggle('d-none', favourite);\n\n const iconSelectsor = favourite ? selectors.elements.favouriteIconActive : selectors.elements.favouriteIconInactive;\n const favouriteIcon = element.querySelector(iconSelectsor);\n element.setAttribute('aria-label', favouriteIcon?.getAttribute('data-action-label') || '');\n });\n }\n\n /**\n * Refresh the favourite content.\n *\n * @param {Array} mappedModules The modules to be displayed in the favourite tab.\n */\n async refreshFavouritesTabContent(mappedModules) {\n const templateData = await this.exporter.getFavouriteTabData(mappedModules);\n const favouriteArea = this.modalBody.querySelector(selectors.regions.favouriteTab);\n const {html, js} = await Templates.renderForPromise(\n 'core_course/local/activitychooser/tabcontent',\n templateData,\n );\n await Templates.replaceNodeContents(favouriteArea, html, js);\n }\n\n /**\n * Toggle the display of the favourite tab.\n *\n * The favourite tab is only displayed when there are favourite modules\n * or when it is the active tab.\n *\n * @param {Boolean} displayed Whether we want to show or hide the favourite tab\n */\n toggleFavouriteTabDisplay(displayed) {\n const favouriteTabNav = this.modalBody.querySelector(selectors.regions.favouriteTabNav);\n\n let moveFocusTo;\n if (!displayed && favouriteTabNav.classList.contains('active')) {\n moveFocusTo = this.showAllActivitiesTab();\n }\n\n favouriteTabNav?.classList.toggle('d-none', !displayed);\n favouriteTabNav.tabIndex = displayed ? 0 : -1;\n // The disabled class is used by Boostrap Tab for keyboard navigation.\n if (displayed) {\n favouriteTabNav.classList.remove('disabled');\n } else {\n favouriteTabNav.classList.add('disabled');\n }\n\n if (moveFocusTo) {\n moveFocusTo.focus();\n }\n this.initActiveTabNavigation();\n }\n\n /**\n * Given an event from the main module 'page' navigate to it's help section via a carousel.\n *\n * @method showModuleHelp\n * @param {Object} moduleData Data of the module to carousel to\n */\n async showModuleHelp(moduleData) {\n const carousel = this.modalBody.querySelector(selectors.regions.carousel);\n\n const help = carousel.querySelector(selectors.regions.help);\n help.innerHTML = '';\n help.classList.add('m-auto');\n\n // Add a spinner.\n const spinnerPromise = addIconToContainer(help);\n\n // Used later...\n let transitionPromiseResolver = null;\n const transitionPromise = new Promise(resolve => {\n transitionPromiseResolver = resolve;\n });\n\n // Build up the html & js ready to place into the help section.\n const contentPromise = Templates.renderForPromise(\n 'core_course/local/activitychooser/help',\n await this.exporter.getModuleHelpTemplateData(moduleData),\n );\n\n // Wait for the content to be ready, and for the transition to be complet.\n Promise.all([contentPromise, spinnerPromise, transitionPromise])\n .then(([{html, js}]) => Templates.replaceNodeContents(help, html, js))\n .then(() => {\n help.querySelector(selectors.regions.chooserSummary.header).focus();\n return help;\n })\n .catch(Notification.exception);\n\n // Move to the next slide, and resolve the transition promise when it's done.\n carousel.addEventListener(\n 'slid.bs.carousel',\n () => {\n transitionPromiseResolver();\n },\n {once: true}\n );\n // Trigger the transition between 'pages'.\n Carousel.getInstance(carousel).next();\n }\n\n /**\n * Hide the help section of the chooser.\n *\n * @param {String|null} internal The internal name of the module to return to, if any.\n */\n hideModuleHelp(internal = null) {\n const carousel = this.modalBody.querySelector(selectors.regions.carousel);\n // Trigger the transition between 'pages'.\n Carousel.getInstance(carousel).to(0);\n // Some active footers may not provide a valid internal value.\n if (!internal) {\n return;\n }\n carousel.addEventListener(\n 'slid.bs.carousel',\n () => {\n this.focusChooserOption(internal);\n },\n {once: true}\n );\n }\n\n /**\n * Set the module data for the back button.\n *\n * @param {Object} moduleData The module data to set for the back button.\n */\n setBackButtonModuleData(moduleData) {\n const footer = getFirst(this.modal.getFooter());\n const modnameValue = `${moduleData.componentname}_${moduleData.link}`;\n footer.querySelector(selectors.actions.closeOption).dataset.modname = modnameValue;\n }\n\n /**\n * Toggle the visibility of the back button.\n *\n * @param {Boolean} show Whether to show or hide the back button.\n */\n toggleBackButton(show) {\n const footer = getFirst(this.modal.getFooter());\n footer.querySelector(selectors.actions.closeOption).classList.toggle('d-none', !show);\n }\n\n /**\n * Toggle the visibility of the \"Add selected\" button.\n *\n * @param {Boolean} show Whether to show or hide the \"Add selected\" button.\n */\n toggleAddButton(show) {\n const footer = getFirst(this.modal.getFooter());\n footer.querySelector(selectors.actions.addSelectedChooserOption).classList.toggle('d-none', !show);\n }\n\n /**\n * Toggle the visibility of the active footer.\n *\n * @param {Boolean} show Whether to show or hide the active footer.\n */\n toggleActiveFooter(show) {\n const footer = getFirst(this.modal.getFooter());\n footer.querySelector(selectors.regions.activeFooter).classList.toggle('d-none', !show);\n }\n\n /**\n * Focus on a specific activity inside the active tab (if present).\n *\n * @private\n * @method focusChooserOption\n * @param {String} internal The internal name of the module.\n */\n focusChooserOption(internal) {\n const currentTabNav = this.modalBody.querySelector(selectors.elements.activetab);\n const activeSectionId = currentTabNav.getAttribute(\"href\");\n const sectionChooserOptions = this.modalBody.querySelector(selectors.regions.getSectionChooserOptions(activeSectionId));\n const newCurrent = sectionChooserOptions.querySelector(selectors.regions.getModuleSelector(internal));\n\n if (!newCurrent) {\n throw new Error(`Invalid chooser option to focus on: ${internal}`);\n }\n\n // Chooser can only have one element focusable at a time, so we disable them all first.\n this.disableFocusAllChooserOptions(currentTabNav);\n this.toggleFocusableChooserOption(newCurrent, true);\n\n // Little hack: we want the element considered a focus-visible element.\n // But the focus method does not trigger the focus-visible class. There's an\n // experimental \"{focusVisible: true}\" option in the focus method, but it's not\n // supported in all browsers yet so we need to fake an editable element.\n newCurrent.contentEditable = true;\n newCurrent.focus();\n newCurrent.contentEditable = false;\n }\n\n /**\n * Initialise the active tab navigation.\n */\n initActiveTabNavigation() {\n const activeSectionId = this.modalBody.querySelector(selectors.elements.activetab).getAttribute(\"href\");\n const sectionChooserOptions = this.modalBody.querySelector(selectors.regions.getSectionChooserOptions(activeSectionId));\n const firstChooserOption = sectionChooserOptions?.querySelector(selectors.regions.chooserOption.container);\n if (!firstChooserOption) {\n return;\n }\n this.toggleFocusableChooserOption(firstChooserOption, true);\n }\n\n /**\n * Initialise all Boostrap components.\n */\n initBootstrapComponents() {\n this.modalBody.querySelectorAll(selectors.elements.tab).forEach((navTab) => {\n // Init the Bootstrap Tab navigation.\n Tab.getOrCreateInstance(navTab);\n });\n\n // Set up the carousel.\n const carousel = this.modalBody.querySelector(selectors.regions.carousel);\n new Carousel(carousel, {\n interval: false,\n pause: true,\n keyboard: false\n });\n }\n\n /**\n * Disable the focus of all chooser options in a specific container (section).\n *\n * @method disableFocusAllChooserOptions\n * @param {HTMLElement} tabNav The tab navigation element (from the shown.bs.ta event).\n */\n disableFocusAllChooserOptions(tabNav) {\n const tabId = tabNav.getAttribute(\"href\");\n const chooserOptions = this.modalBody.querySelector(\n selectors.regions.getSectionChooserOptions(tabId)\n );\n\n if (chooserOptions === null) {\n return;\n }\n\n const allChooserOptions = chooserOptions.querySelectorAll(selectors.regions.chooserOption.container);\n allChooserOptions.forEach((chooserOption) => {\n this.toggleFocusableChooserOption(chooserOption, false);\n });\n }\n\n /**\n * Add or remove a chooser option from the focus order.\n *\n * @private\n * @method toggleFocusableChooserOption\n * @param {HTMLElement} chooserOption The chooser option element which should be added or removed from the focus order\n * @param {Boolean} isFocusable Whether the chooser element is focusable or not\n */\n toggleFocusableChooserOption(chooserOption, isFocusable) {\n const chooserOptionLink = chooserOption.querySelector(selectors.actions.addChooser);\n const chooserOptionHelp = chooserOption.querySelector(selectors.actions.optionActions.showSummary);\n const chooserOptionFavourite = chooserOption.querySelector(selectors.actions.optionActions.manageFavourite);\n\n if (isFocusable) {\n // Set tabindex to 0 to add current chooser option element to the focus order.\n chooserOption.tabIndex = 0;\n chooserOptionLink.tabIndex = 0;\n chooserOptionHelp.tabIndex = 0;\n chooserOptionFavourite.tabIndex = 0;\n } else {\n // Set tabindex to -1 to remove the previous chooser option element from the focus order.\n chooserOption.tabIndex = -1;\n chooserOptionLink.tabIndex = -1;\n chooserOptionHelp.tabIndex = -1;\n chooserOptionFavourite.tabIndex = -1;\n }\n }\n\n /**\n * Move the focus to the previous chooser option element.\n *\n * @param {HTMLElement} current The current chooser option element\n */\n focusNextChooserOption(current) {\n this.moveChooserOptionFocus(\n current,\n (currentOption) => currentOption.nextElementSibling ?? currentOption,\n );\n }\n\n /**\n * Move the focus to the previous chooser option element.\n *\n * @param {HTMLElement} current The current chooser option element\n */\n focusPreviousChooserOption(current) {\n this.moveChooserOptionFocus(\n current,\n (currentOption) => currentOption.previousElementSibling ?? currentOption,\n );\n }\n\n /**\n * Move the focus to the first chooser option element.\n *\n * @param {HTMLElement} current The current chooser option element\n */\n focusFirstChooserOption(current) {\n this.moveChooserOptionFocus(\n current,\n (currentOption, container) => container.firstElementChild ?? currentOption,\n );\n }\n\n /**\n * Move the focus to the last chooser option element.\n *\n * @param {HTMLElement} current The current chooser option element\n */\n focusLastChooserOption(current) {\n this.moveChooserOptionFocus(\n current,\n (currentOption, container) => container.lastElementChild ?? currentOption,\n );\n }\n\n /**\n * Move the focus to the next chooser option element.\n *\n * @private\n * @param {HTMLElement} current The current chooser option element\n * @param {Function} getNextFocus Function to get the next focusable element\n */\n moveChooserOptionFocus(current, getNextFocus) {\n const currentOption = this.getClosestChooserOption(current);\n const container = current.closest(selectors.regions.chooserOptions);\n\n if (!container || !currentOption) {\n throw new Error('Invalid chooser options container or current option');\n }\n\n const newFocusOption = getNextFocus(currentOption, container);\n if (!newFocusOption) {\n return;\n }\n\n this.toggleFocusableChooserOption(currentOption, false);\n this.toggleFocusableChooserOption(newFocusOption, true);\n newFocusOption.focus();\n }\n\n /**\n * Mark a chooser option as selected.\n *\n * @param {HTMLElement} chooserOption The chooser option element to mark as selected.\n */\n markChooserOptionAsSelected(chooserOption) {\n this.unmarkAllChooserOptionAsSelected();\n chooserOption.classList.add('selected');\n chooserOption.classList.add('border-primary');\n chooserOption.setAttribute('aria-selected', 'true');\n\n const footer = getFirst(this.modal.getFooter());\n const addButton = footer.querySelector(selectors.actions.addSelectedChooserOption);\n if (addButton) {\n addButton.removeAttribute('disabled');\n }\n }\n\n /**\n * Unmark all chooser options as selected.\n */\n unmarkAllChooserOptionAsSelected() {\n const selectedOptions = this.modalBody.querySelectorAll(`${selectors.regions.chooserOption.container}.selected`);\n selectedOptions.forEach((option) => {\n option.classList.remove('selected');\n option.classList.remove('border-primary');\n option.setAttribute('aria-selected', 'false');\n });\n\n const footer = getFirst(this.modal.getFooter());\n const addButton = footer.querySelector(selectors.actions.addSelectedChooserOption);\n if (addButton) {\n addButton.setAttribute('disabled', 'disabled');\n }\n }\n}\n"],"names":["constructor","dialogue","modal","exporter","modalBody","this","getBody","getSearchInputElement","querySelector","selectors","actions","search","getClosestChooserOption","element","closest","regions","chooserOption","container","isFavoutiteTabActive","favouriteTab","favouriteTabNav","classList","contains","getChooserOptionUrl","optionContainer","optionLink","addChooser","Error","getAttribute","searchResultsData","searchResultsContainer","searchResults","clearSearchButton","clearSearch","renderSearchResults","firstSearchResultItem","chooserOptions","toggleFocusableChooserOption","remove","tabContent","categoryContent","add","cleanSearchResults","innerHTML","templateData","getSearchResultData","html","js","Templates","renderForPromise","replaceNodeContents","showAllActivitiesTab","navTab","allTabNav","pendingPromise","Pending","addEventListener","resolve","once","getOrCreateInstance","show","updateItemStarredIcons","internal","favourite","favouriteButtons","querySelectorAll","elements","moduleItem","optionActions","manageFavourite","Array","from","forEach","toggle","dataset","favourited","setAttribute","favouriteIconActive","favouriteIconInactive","iconSelectsor","favouriteIcon","mappedModules","getFavouriteTabData","favouriteArea","toggleFavouriteTabDisplay","displayed","moveFocusTo","tabIndex","focus","initActiveTabNavigation","moduleData","carousel","help","spinnerPromise","transitionPromiseResolver","transitionPromise","Promise","contentPromise","getModuleHelpTemplateData","all","then","_ref","chooserSummary","header","catch","Notification","exception","getInstance","next","hideModuleHelp","to","focusChooserOption","setBackButtonModuleData","footer","getFooter","modnameValue","componentname","link","closeOption","modname","toggleBackButton","toggleAddButton","addSelectedChooserOption","toggleActiveFooter","activeFooter","currentTabNav","activetab","activeSectionId","newCurrent","getSectionChooserOptions","getModuleSelector","disableFocusAllChooserOptions","contentEditable","sectionChooserOptions","firstChooserOption","initBootstrapComponents","tab","Carousel","interval","pause","keyboard","tabNav","tabId","isFocusable","chooserOptionLink","chooserOptionHelp","showSummary","chooserOptionFavourite","focusNextChooserOption","current","moveChooserOptionFocus","currentOption","nextElementSibling","focusPreviousChooserOption","previousElementSibling","focusFirstChooserOption","firstElementChild","focusLastChooserOption","lastElementChild","getNextFocus","newFocusOption","markChooserOptionAsSelected","unmarkAllChooserOptionAsSelected","addButton","removeAttribute","option"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"dialoguedom.min.js","sources":["../../../src/local/activitychooser/dialoguedom.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\nimport {addIconToContainer} from 'core/loadingicon';\nimport Carousel from 'theme_boost/bootstrap/carousel';\nimport {getFirst} from 'core/normalise';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport selectors from 'core_course/local/activitychooser/selectors';\nimport Tab from 'theme_boost/bootstrap/tab';\nimport * as Templates from 'core/templates';\n\n\n/**\n * The activity changer dialogue DOM manipulation module.\n *\n * @module core_course/local/activitychooser/dialoguedom\n * @copyright 2025 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nexport default class ChooserDialogueDOM {\n /**\n * ChooserDialogueDOM constructor.\n *\n * @param {Object} dialogue The dialogue object.\n * @param {Modal} modal The modal object.\n * @param {Object} exporter The exporter object to generate template data.\n */\n constructor(dialogue, modal, exporter) {\n this.modal = modal;\n this.modalBody = getFirst(this.modal.getBody());\n this.exporter = exporter;\n // Temporal variable while migrating methods.\n this.dialogue = dialogue;\n }\n\n /**\n * Get the search input element.\n *\n * @return {HTMLElement} The search input element.\n */\n getSearchInputElement() {\n return this.modalBody.querySelector(selectors.actions.search);\n }\n\n /**\n * Get the closest chooser option element.\n *\n * @param {HTMLElement} element\n * @return {HTMLElement|null} element\n */\n getClosestChooserOption(element) {\n return element.closest(selectors.regions.chooserOption.container);\n }\n\n /**\n * Check if the search tab is active.\n *\n * @return {Boolean} True if the search tab is active, false otherwise.\n */\n isFavoutiteTabActive() {\n const favouriteTab = this.modalBody.querySelector(selectors.regions.favouriteTabNav);\n return favouriteTab && favouriteTab.classList.contains('active');\n }\n\n /**\n * Get the URL of a chooser option.\n *\n * @param {HTMLElement} optionContainer The container of the chooser option.\n * @return {String} The URL of the chooser option.\n * @throws {Error} If the option container does not contain a link.\n */\n getChooserOptionUrl(optionContainer) {\n const optionLink = optionContainer.querySelector(selectors.actions.addChooser);\n if (!optionLink) {\n throw new Error('Invalid chooser option container: no link found');\n }\n return optionLink.getAttribute('href');\n }\n\n /**\n * Show the search results.\n *\n * @param {Object} searchResultsData Data containing the module items that satisfy the search criteria\n */\n async refreshSearchResults(searchResultsData) {\n const searchResultsContainer = this.modalBody.querySelector(selectors.regions.searchResults);\n const clearSearchButton = this.modalBody.querySelector(selectors.actions.clearSearch);\n\n await this.renderSearchResults(searchResultsContainer, searchResultsData);\n const chooserOptionsContainer = searchResultsContainer.querySelector(selectors.regions.chooserOptions);\n const firstSearchResultItem = chooserOptionsContainer.querySelector(selectors.regions.chooserOption.container);\n if (firstSearchResultItem) {\n // Set the first result item to be focusable.\n this.toggleFocusableChooserOption(firstSearchResultItem, true);\n // Register keyboard events on the created search result items.\n }\n clearSearchButton.classList.remove('d-none');\n\n // Results are rendered in the all activities tab, so we need to hide the category content.\n const tabContent = searchResultsContainer.closest(selectors.regions.tabContent);\n const categoryContent = tabContent.querySelector(selectors.regions.categoryContent);\n categoryContent.classList.add('d-none');\n }\n\n /**\n * Clear the search results.\n */\n cleanSearchResults() {\n const searchResultsContainer = this.modalBody.querySelector(selectors.regions.searchResults);\n const clearSearchButton = this.modalBody.querySelector(selectors.actions.clearSearch);\n searchResultsContainer.innerHTML = '';\n clearSearchButton.classList.add('d-none');\n\n // Results are rendered in the all activities tab, so we need to show the category content again.\n const tabContent = searchResultsContainer.closest(selectors.regions.tabContent);\n const categoryContent = tabContent.querySelector(selectors.regions.categoryContent);\n categoryContent.classList.remove('d-none');\n }\n\n /**\n * Render the search results in a defined container\n *\n * @private\n * @method renderSearchResults\n * @param {HTMLElement} searchResultsContainer The container where the data should be rendered\n * @param {Object} searchResultsData Data containing the module items that satisfy the search criteria\n */\n async renderSearchResults(searchResultsContainer, searchResultsData) {\n const templateData = this.exporter.getSearchResultData(searchResultsData);\n // Build up the html & js ready to place into the help section.\n const {html, js} = await Templates.renderForPromise(\n 'core_course/local/activitychooser/search_results',\n templateData\n );\n await Templates.replaceNodeContents(searchResultsContainer, html, js);\n }\n\n /**\n * Show the \"All activities\" tab.\n *\n * @method showAllActivitiesTab\n * @return {HTMLElement} The \"All activities\" tab element.\n */\n showAllActivitiesTab() {\n const navTab = this.modalBody.querySelector(selectors.regions.allTabNav);\n\n if (navTab.classList.contains('active')) {\n return navTab;\n }\n\n const pendingPromise = new Pending('core_course/activitychooser:alltab');\n navTab.addEventListener('shown.bs.tab', pendingPromise.resolve, {once: true});\n\n Tab.getOrCreateInstance(navTab).show();\n return navTab;\n }\n\n /**\n * Update the starred icons in the chooser modal.\n *\n * @method updateItemStarredIcons\n * @param {String} internal The internal name of the module.\n * @param {Boolean} favourite Whether the module is a favourite or not.\n */\n updateItemStarredIcons(internal, favourite) {\n const favouriteButtons = this.modalBody.querySelectorAll(\n `${selectors.elements.moduleItem(internal)} ${selectors.actions.optionActions.manageFavourite}`\n );\n Array.from(favouriteButtons).forEach((element) => {\n element.dataset.favourited = favourite;\n element.setAttribute('aria-pressed', favourite);\n element.querySelector(selectors.elements.favouriteIconActive)?.classList.toggle('d-none', !favourite);\n element.querySelector(selectors.elements.favouriteIconInactive)?.classList.toggle('d-none', favourite);\n\n const iconSelectsor = favourite ? selectors.elements.favouriteIconActive : selectors.elements.favouriteIconInactive;\n const favouriteIcon = element.querySelector(iconSelectsor);\n element.setAttribute('aria-label', favouriteIcon?.getAttribute('data-action-label') || '');\n element.setAttribute('title', favouriteIcon?.getAttribute('data-action-label') || '');\n });\n }\n\n /**\n * Refresh the favourite content.\n *\n * @param {Array} mappedModules The modules to be displayed in the favourite tab.\n */\n async refreshFavouritesTabContent(mappedModules) {\n const templateData = await this.exporter.getFavouriteTabData(mappedModules);\n const favouriteArea = this.modalBody.querySelector(selectors.regions.favouriteTab);\n const {html, js} = await Templates.renderForPromise(\n 'core_course/local/activitychooser/tabcontent',\n templateData,\n );\n await Templates.replaceNodeContents(favouriteArea, html, js);\n }\n\n /**\n * Toggle the display of the favourite tab.\n *\n * The favourite tab is only displayed when there are favourite modules\n * or when it is the active tab.\n *\n * @param {Boolean} displayed Whether we want to show or hide the favourite tab\n */\n toggleFavouriteTabDisplay(displayed) {\n const favouriteTabNav = this.modalBody.querySelector(selectors.regions.favouriteTabNav);\n\n let moveFocusTo;\n if (!displayed && favouriteTabNav.classList.contains('active')) {\n moveFocusTo = this.showAllActivitiesTab();\n }\n\n favouriteTabNav?.classList.toggle('d-none', !displayed);\n favouriteTabNav.tabIndex = displayed ? 0 : -1;\n // The disabled class is used by Boostrap Tab for keyboard navigation.\n if (displayed) {\n favouriteTabNav.classList.remove('disabled');\n } else {\n favouriteTabNav.classList.add('disabled');\n }\n\n if (moveFocusTo) {\n moveFocusTo.focus();\n }\n this.initActiveTabNavigation();\n }\n\n /**\n * Given an event from the main module 'page' navigate to it's help section via a carousel.\n *\n * @method showModuleHelp\n * @param {Object} moduleData Data of the module to carousel to\n */\n async showModuleHelp(moduleData) {\n const carousel = this.modalBody.querySelector(selectors.regions.carousel);\n\n const help = carousel.querySelector(selectors.regions.help);\n help.innerHTML = '';\n help.classList.add('m-auto');\n\n // Add a spinner.\n const spinnerPromise = addIconToContainer(help);\n\n // Used later...\n let transitionPromiseResolver = null;\n const transitionPromise = new Promise(resolve => {\n transitionPromiseResolver = resolve;\n });\n\n // Build up the html & js ready to place into the help section.\n const contentPromise = Templates.renderForPromise(\n 'core_course/local/activitychooser/help',\n await this.exporter.getModuleHelpTemplateData(moduleData),\n );\n\n // Wait for the content to be ready, and for the transition to be complet.\n Promise.all([contentPromise, spinnerPromise, transitionPromise])\n .then(([{html, js}]) => Templates.replaceNodeContents(help, html, js))\n .then(() => {\n help.querySelector(selectors.regions.chooserSummary.header).focus();\n return help;\n })\n .catch(Notification.exception);\n\n // Move to the next slide, and resolve the transition promise when it's done.\n carousel.addEventListener(\n 'slid.bs.carousel',\n () => {\n transitionPromiseResolver();\n },\n {once: true}\n );\n // Trigger the transition between 'pages'.\n Carousel.getInstance(carousel).next();\n }\n\n /**\n * Hide the help section of the chooser.\n *\n * @param {String|null} internal The internal name of the module to return to, if any.\n */\n hideModuleHelp(internal = null) {\n const carousel = this.modalBody.querySelector(selectors.regions.carousel);\n // Trigger the transition between 'pages'.\n Carousel.getInstance(carousel).to(0);\n // Some active footers may not provide a valid internal value.\n if (!internal) {\n return;\n }\n carousel.addEventListener(\n 'slid.bs.carousel',\n () => {\n this.focusChooserOption(internal);\n },\n {once: true}\n );\n }\n\n /**\n * Set the module data for the back button.\n *\n * @param {Object} moduleData The module data to set for the back button.\n */\n setBackButtonModuleData(moduleData) {\n const footer = getFirst(this.modal.getFooter());\n const modnameValue = `${moduleData.componentname}_${moduleData.link}`;\n footer.querySelector(selectors.actions.closeOption).dataset.modname = modnameValue;\n }\n\n /**\n * Toggle the visibility of the back button.\n *\n * @param {Boolean} show Whether to show or hide the back button.\n */\n toggleBackButton(show) {\n const footer = getFirst(this.modal.getFooter());\n footer.querySelector(selectors.actions.closeOption).classList.toggle('d-none', !show);\n }\n\n /**\n * Toggle the visibility of the \"Add selected\" button.\n *\n * @param {Boolean} show Whether to show or hide the \"Add selected\" button.\n */\n toggleAddButton(show) {\n const footer = getFirst(this.modal.getFooter());\n footer.querySelector(selectors.actions.addSelectedChooserOption).classList.toggle('d-none', !show);\n }\n\n /**\n * Toggle the visibility of the active footer.\n *\n * @param {Boolean} show Whether to show or hide the active footer.\n */\n toggleActiveFooter(show) {\n const footer = getFirst(this.modal.getFooter());\n footer.querySelector(selectors.regions.activeFooter).classList.toggle('d-none', !show);\n }\n\n /**\n * Focus on a specific activity inside the active tab (if present).\n *\n * @private\n * @method focusChooserOption\n * @param {String} internal The internal name of the module.\n */\n focusChooserOption(internal) {\n const currentTabNav = this.modalBody.querySelector(selectors.elements.activetab);\n const activeSectionId = currentTabNav.getAttribute(\"href\");\n const sectionChooserOptions = this.modalBody.querySelector(selectors.regions.getSectionChooserOptions(activeSectionId));\n const newCurrent = sectionChooserOptions.querySelector(selectors.regions.getModuleSelector(internal));\n\n if (!newCurrent) {\n throw new Error(`Invalid chooser option to focus on: ${internal}`);\n }\n\n // Chooser can only have one element focusable at a time, so we disable them all first.\n this.disableFocusAllChooserOptions(currentTabNav);\n this.toggleFocusableChooserOption(newCurrent, true);\n\n // Little hack: we want the element considered a focus-visible element.\n // But the focus method does not trigger the focus-visible class. There's an\n // experimental \"{focusVisible: true}\" option in the focus method, but it's not\n // supported in all browsers yet so we need to fake an editable element.\n newCurrent.contentEditable = true;\n newCurrent.focus();\n newCurrent.contentEditable = false;\n }\n\n /**\n * Initialise the active tab navigation.\n */\n initActiveTabNavigation() {\n const activeSectionId = this.modalBody.querySelector(selectors.elements.activetab).getAttribute(\"href\");\n const sectionChooserOptions = this.modalBody.querySelector(selectors.regions.getSectionChooserOptions(activeSectionId));\n const firstChooserOption = sectionChooserOptions?.querySelector(selectors.regions.chooserOption.container);\n if (!firstChooserOption) {\n return;\n }\n this.toggleFocusableChooserOption(firstChooserOption, true);\n }\n\n /**\n * Initialise all Boostrap components.\n */\n initBootstrapComponents() {\n this.modalBody.querySelectorAll(selectors.elements.tab).forEach((navTab) => {\n // Init the Bootstrap Tab navigation.\n Tab.getOrCreateInstance(navTab);\n });\n\n // Set up the carousel.\n const carousel = this.modalBody.querySelector(selectors.regions.carousel);\n new Carousel(carousel, {\n interval: false,\n pause: true,\n keyboard: false\n });\n }\n\n /**\n * Disable the focus of all chooser options in a specific container (section).\n *\n * @method disableFocusAllChooserOptions\n * @param {HTMLElement} tabNav The tab navigation element (from the shown.bs.ta event).\n */\n disableFocusAllChooserOptions(tabNav) {\n const tabId = tabNav.getAttribute(\"href\");\n const chooserOptions = this.modalBody.querySelector(\n selectors.regions.getSectionChooserOptions(tabId)\n );\n\n if (chooserOptions === null) {\n return;\n }\n\n const allChooserOptions = chooserOptions.querySelectorAll(selectors.regions.chooserOption.container);\n allChooserOptions.forEach((chooserOption) => {\n this.toggleFocusableChooserOption(chooserOption, false);\n });\n }\n\n /**\n * Add or remove a chooser option from the focus order.\n *\n * @private\n * @method toggleFocusableChooserOption\n * @param {HTMLElement} chooserOption The chooser option element which should be added or removed from the focus order\n * @param {Boolean} isFocusable Whether the chooser element is focusable or not\n */\n toggleFocusableChooserOption(chooserOption, isFocusable) {\n const chooserOptionLink = chooserOption.querySelector(selectors.actions.addChooser);\n const chooserOptionHelp = chooserOption.querySelector(selectors.actions.optionActions.showSummary);\n const chooserOptionFavourite = chooserOption.querySelector(selectors.actions.optionActions.manageFavourite);\n\n if (isFocusable) {\n // Set tabindex to 0 to add current chooser option element to the focus order.\n chooserOption.tabIndex = 0;\n chooserOptionLink.tabIndex = 0;\n chooserOptionHelp.tabIndex = 0;\n chooserOptionFavourite.tabIndex = 0;\n } else {\n // Set tabindex to -1 to remove the previous chooser option element from the focus order.\n chooserOption.tabIndex = -1;\n chooserOptionLink.tabIndex = -1;\n chooserOptionHelp.tabIndex = -1;\n chooserOptionFavourite.tabIndex = -1;\n }\n }\n\n /**\n * Move the focus to the previous chooser option element.\n *\n * @param {HTMLElement} current The current chooser option element\n */\n focusNextChooserOption(current) {\n this.moveChooserOptionFocus(\n current,\n (currentOption) => currentOption.nextElementSibling ?? currentOption,\n );\n }\n\n /**\n * Move the focus to the previous chooser option element.\n *\n * @param {HTMLElement} current The current chooser option element\n */\n focusPreviousChooserOption(current) {\n this.moveChooserOptionFocus(\n current,\n (currentOption) => currentOption.previousElementSibling ?? currentOption,\n );\n }\n\n /**\n * Move the focus to the first chooser option element.\n *\n * @param {HTMLElement} current The current chooser option element\n */\n focusFirstChooserOption(current) {\n this.moveChooserOptionFocus(\n current,\n (currentOption, container) => container.firstElementChild ?? currentOption,\n );\n }\n\n /**\n * Move the focus to the last chooser option element.\n *\n * @param {HTMLElement} current The current chooser option element\n */\n focusLastChooserOption(current) {\n this.moveChooserOptionFocus(\n current,\n (currentOption, container) => container.lastElementChild ?? currentOption,\n );\n }\n\n /**\n * Move the focus to the next chooser option element.\n *\n * @private\n * @param {HTMLElement} current The current chooser option element\n * @param {Function} getNextFocus Function to get the next focusable element\n */\n moveChooserOptionFocus(current, getNextFocus) {\n const currentOption = this.getClosestChooserOption(current);\n const container = current.closest(selectors.regions.chooserOptions);\n\n if (!container || !currentOption) {\n throw new Error('Invalid chooser options container or current option');\n }\n\n const newFocusOption = getNextFocus(currentOption, container);\n if (!newFocusOption) {\n return;\n }\n\n this.toggleFocusableChooserOption(currentOption, false);\n this.toggleFocusableChooserOption(newFocusOption, true);\n newFocusOption.focus();\n }\n\n /**\n * Mark a chooser option as selected.\n *\n * @param {HTMLElement} chooserOption The chooser option element to mark as selected.\n */\n markChooserOptionAsSelected(chooserOption) {\n this.unmarkAllChooserOptionAsSelected();\n chooserOption.classList.add('selected');\n chooserOption.setAttribute('aria-selected', 'true');\n\n const footer = getFirst(this.modal.getFooter());\n const addButton = footer.querySelector(selectors.actions.addSelectedChooserOption);\n if (addButton) {\n addButton.removeAttribute('disabled');\n }\n }\n\n /**\n * Unmark all chooser options as selected.\n */\n unmarkAllChooserOptionAsSelected() {\n const selectedOptions = this.modalBody.querySelectorAll(`${selectors.regions.chooserOption.container}.selected`);\n selectedOptions.forEach((option) => {\n option.classList.remove('selected');\n option.setAttribute('aria-selected', 'false');\n });\n\n const footer = getFirst(this.modal.getFooter());\n const addButton = footer.querySelector(selectors.actions.addSelectedChooserOption);\n if (addButton) {\n addButton.setAttribute('disabled', 'disabled');\n }\n }\n}\n"],"names":["constructor","dialogue","modal","exporter","modalBody","this","getBody","getSearchInputElement","querySelector","selectors","actions","search","getClosestChooserOption","element","closest","regions","chooserOption","container","isFavoutiteTabActive","favouriteTab","favouriteTabNav","classList","contains","getChooserOptionUrl","optionContainer","optionLink","addChooser","Error","getAttribute","searchResultsData","searchResultsContainer","searchResults","clearSearchButton","clearSearch","renderSearchResults","firstSearchResultItem","chooserOptions","toggleFocusableChooserOption","remove","tabContent","categoryContent","add","cleanSearchResults","innerHTML","templateData","getSearchResultData","html","js","Templates","renderForPromise","replaceNodeContents","showAllActivitiesTab","navTab","allTabNav","pendingPromise","Pending","addEventListener","resolve","once","getOrCreateInstance","show","updateItemStarredIcons","internal","favourite","favouriteButtons","querySelectorAll","elements","moduleItem","optionActions","manageFavourite","Array","from","forEach","dataset","favourited","setAttribute","favouriteIconActive","toggle","favouriteIconInactive","iconSelectsor","favouriteIcon","mappedModules","getFavouriteTabData","favouriteArea","toggleFavouriteTabDisplay","displayed","moveFocusTo","tabIndex","focus","initActiveTabNavigation","moduleData","carousel","help","spinnerPromise","transitionPromiseResolver","transitionPromise","Promise","contentPromise","getModuleHelpTemplateData","all","then","_ref","chooserSummary","header","catch","Notification","exception","getInstance","next","hideModuleHelp","to","focusChooserOption","setBackButtonModuleData","footer","getFooter","modnameValue","componentname","link","closeOption","modname","toggleBackButton","toggleAddButton","addSelectedChooserOption","toggleActiveFooter","activeFooter","currentTabNav","activetab","activeSectionId","newCurrent","getSectionChooserOptions","getModuleSelector","disableFocusAllChooserOptions","contentEditable","sectionChooserOptions","firstChooserOption","initBootstrapComponents","tab","Carousel","interval","pause","keyboard","tabNav","tabId","isFocusable","chooserOptionLink","chooserOptionHelp","showSummary","chooserOptionFavourite","focusNextChooserOption","current","moveChooserOptionFocus","currentOption","nextElementSibling","focusPreviousChooserOption","previousElementSibling","focusFirstChooserOption","firstElementChild","focusLastChooserOption","lastElementChild","getNextFocus","newFocusOption","markChooserOptionAsSelected","unmarkAllChooserOptionAsSelected","addButton","removeAttribute","option"],"mappings":"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"} \ No newline at end of file diff --git a/public/course/amd/build/local/activitychooser/exporter.min.js b/public/course/amd/build/local/activitychooser/exporter.min.js index 94f42f63cc9..426c69df7ce 100644 --- a/public/course/amd/build/local/activitychooser/exporter.min.js +++ b/public/course/amd/build/local/activitychooser/exporter.min.js @@ -6,6 +6,6 @@ define("core_course/local/activitychooser/exporter",["exports","core/str"],(func * @copyright 2025 Ferran Recio * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -const activityCategories=["administration","assessment","collaboration","communication","content","interactivecontent"];let allStrings=null;loadNecessaryStrings();async function loadNecessaryStrings(){if(null!==allStrings)return allStrings;allStrings={};const stringToLoad=[{key:"all",component:"core"},{key:"favourites",component:"core"},{key:"recommended",component:"core"},{key:"recommended_help",component:"core_course"},{key:"mod_purposes",component:"core_course"},...activityCategories.map((key=>({key:"mod_purpose_"+key,component:"core_course"}))),...activityCategories.map((key=>({key:"mod_purpose_"+key+"_help",component:"core_course"})))],loadedStrings=await(0,_str.getStrings)(stringToLoad);return stringToLoad.forEach(((_ref,index)=>{let{key:key}=_ref;allStrings[key]=loadedStrings[index]})),allStrings}return _exports.default=class{getTabData(tabId,filteredModules,tabLabel){let tabHelp=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null;const result={tabId:tabId,active:arguments.length>4&&void 0!==arguments[4]&&arguments[4],items:filteredModules,displayed:filteredModules.length>0,tabLabel:tabLabel};return tabHelp&&(result.tabHelp=tabHelp),result}normaliseModulesData(modulesData){if(modulesData instanceof Map)modulesData=Array.from(modulesData.values());else if(!Array.isArray(modulesData))throw new Error("Invalid modules data format. Expected an array or a Map.");return modulesData}async getModChooserTemplateData(modulesData){modulesData=this.normaliseModulesData(modulesData);const allStrings=await loadNecessaryStrings(),favouriteTab=await this.getFavouriteTabData(modulesData),tabs=[{...this.getTabData("all",modulesData,allStrings.all,null,!favouriteTab.displayed),hasSearchResults:!0},favouriteTab,{...this.getTabData("recommended",modulesData.filter((mod=>!0===mod.recommended)),allStrings.recommended,allStrings.recommended_help),separator:!0}];return activityCategories.forEach((category=>{const categoryModules=modulesData.filter((mod=>mod.purpose==category));0!==categoryModules.length&&tabs.push(this.getTabData(category,categoryModules,allStrings["mod_purpose_"+category],allStrings["mod_purpose_"+category+"_help"]))})),{modules:modulesData,tabs:tabs}}async getModuleHelpTemplateData(moduleData){const purposeKey="mod_purpose_".concat(moduleData.purpose),allStrings=await loadNecessaryStrings(),data={...moduleData};return data.details=[{icon:"i/categoryevent",label:allStrings.mod_purposes,value:allStrings[purposeKey]||""}],data}async getFavouriteTabData(modulesData){modulesData=this.normaliseModulesData(modulesData);const allStrings=await loadNecessaryStrings(),favouriteModules=modulesData.filter((mod=>!0===mod.favourite));return this.getTabData("favourites",favouriteModules,allStrings.favourites,null,favouriteModules.length>0)}getSearchResultData(resultsModulesData){return{searchresultsnumber:(resultsModulesData=this.normaliseModulesData(resultsModulesData)).length,searchresults:resultsModulesData}}countTabItems(tabData){var _tabData$items$length,_tabData$items;return null!==(_tabData$items$length=null===(_tabData$items=tabData.items)||void 0===_tabData$items?void 0:_tabData$items.length)&&void 0!==_tabData$items$length?_tabData$items$length:0}getFooterData(footerData){return{activeFooter:footerData.customfootertemplate}}},_exports.default})); +const activityCategories=["administration","assessment","collaboration","communication","content","interactivecontent"];let allStrings=null;loadNecessaryStrings();async function loadNecessaryStrings(){if(null!==allStrings)return allStrings;allStrings={};const stringToLoad=[{key:"all",component:"core"},{key:"favourites",component:"core"},{key:"recommended",component:"core"},{key:"recommended_help",component:"core_course"},{key:"supports",component:"core_course"},...activityCategories.map((key=>({key:"mod_purpose_"+key,component:"core_course"}))),...activityCategories.map((key=>({key:"mod_purpose_"+key+"_help",component:"core_course"})))],loadedStrings=await(0,_str.getStrings)(stringToLoad);return stringToLoad.forEach(((_ref,index)=>{let{key:key}=_ref;allStrings[key]=loadedStrings[index]})),allStrings}return _exports.default=class{getTabData(tabId,filteredModules,tabLabel){let tabHelp=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null;const result={tabId:tabId,active:arguments.length>4&&void 0!==arguments[4]&&arguments[4],items:filteredModules,displayed:filteredModules.length>0,tabLabel:tabLabel};return tabHelp&&(result.tabHelp=tabHelp),result}normaliseModulesData(modulesData){if(modulesData instanceof Map)modulesData=Array.from(modulesData.values());else if(!Array.isArray(modulesData))throw new Error("Invalid modules data format. Expected an array or a Map.");return modulesData}async getModChooserTemplateData(modulesData){modulesData=this.normaliseModulesData(modulesData);const allStrings=await loadNecessaryStrings(),favouriteTab=await this.getFavouriteTabData(modulesData),tabs=[{...this.getTabData("all",modulesData,allStrings.all,null,!favouriteTab.displayed),hasSearchResults:!0},favouriteTab,{...this.getTabData("recommended",modulesData.filter((mod=>!0===mod.recommended)),allStrings.recommended,allStrings.recommended_help),separator:!0}];return activityCategories.forEach((category=>{const categoryModules=modulesData.filter((mod=>mod.purpose==category));0!==categoryModules.length&&tabs.push(this.getTabData(category,categoryModules,allStrings["mod_purpose_"+category],allStrings["mod_purpose_"+category+"_help"]))})),{modules:modulesData,tabs:tabs}}async getModuleHelpTemplateData(moduleData){const purposeKey="mod_purpose_".concat(moduleData.purpose),allStrings=await loadNecessaryStrings(),data={...moduleData};return data.details=[{label:allStrings.supports,value:allStrings[purposeKey]||""}],data}async getFavouriteTabData(modulesData){modulesData=this.normaliseModulesData(modulesData);const allStrings=await loadNecessaryStrings(),favouriteModules=modulesData.filter((mod=>!0===mod.favourite));return this.getTabData("favourites",favouriteModules,allStrings.favourites,null,favouriteModules.length>0)}getSearchResultData(resultsModulesData){return{searchresultsnumber:(resultsModulesData=this.normaliseModulesData(resultsModulesData)).length,searchresults:resultsModulesData}}countTabItems(tabData){var _tabData$items$length,_tabData$items;return null!==(_tabData$items$length=null===(_tabData$items=tabData.items)||void 0===_tabData$items?void 0:_tabData$items.length)&&void 0!==_tabData$items$length?_tabData$items$length:0}getFooterData(footerData){return{activeFooter:footerData.customfootertemplate}}},_exports.default})); //# sourceMappingURL=exporter.min.js.map \ No newline at end of file diff --git a/public/course/amd/build/local/activitychooser/exporter.min.js.map b/public/course/amd/build/local/activitychooser/exporter.min.js.map index e136dda6388..1592eeada0c 100644 --- a/public/course/amd/build/local/activitychooser/exporter.min.js.map +++ b/public/course/amd/build/local/activitychooser/exporter.min.js.map @@ -1 +1 @@ -{"version":3,"file":"exporter.min.js","sources":["../../../src/local/activitychooser/exporter.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 * Module to generate template data for the activity chooser.\n *\n * @module core_course/local/activitychooser/exporter\n * @copyright 2025 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {getStrings} from 'core/str';\n\nconst activityCategories = [\n 'administration',\n 'assessment',\n 'collaboration',\n 'communication',\n 'content',\n 'interactivecontent',\n];\n\nlet allStrings = null;\n\nloadNecessaryStrings();\n\nexport default class {\n /**\n * A tab data structure.\n *\n * @typedef {object} TabData\n * @property {String} tabId the tab ID\n * @property {Boolean} active whether the tab is active or not\n * @property {Array} items the filtered modules to be displayed in the tab\n * @property {Boolean} displayed whether the tab is displayed or not\n * @property {String} tabLabel the tab label\n * @property {String|null} tabHelp the help text for the tab (optional)\n */\n\n /**\n * Generate a tab data object for the activity chooser.\n *\n * @private\n * @param {String} tabId Tab ID.\n * @param {Array} filteredModules Filtered modules to be displayed in the tab.\n * @param {String} tabLabel Tab label.\n * @param {String|null} tabHelp Help text for the tab (optional).\n * @param {Boolean} active Whether the tab is active or not.\n * @return {TabData} Tab data object.\n */\n getTabData(tabId, filteredModules, tabLabel, tabHelp = null, active = false) {\n const result = {\n tabId: tabId,\n active: active,\n items: filteredModules,\n displayed: filteredModules.length > 0,\n tabLabel,\n };\n if (tabHelp) {\n result.tabHelp = tabHelp;\n }\n return result;\n }\n\n /**\n * Normalise the modules data to be used in the chooser.\n *\n * The modulesData can be a plain array or a Map. This method will convert it to a\n * plain array of objects.\n *\n * @param {Array|Map} modulesData Modules data to be used in the chooser.\n * @return {Array} Normalised modules data.\n */\n normaliseModulesData(modulesData) {\n if (modulesData instanceof Map) {\n modulesData = Array.from(modulesData.values());\n } else if (!Array.isArray(modulesData)) {\n throw new Error('Invalid modules data format. Expected an array or a Map.');\n }\n return modulesData;\n }\n\n /**\n * Fetch the chooser template data for a specific section.\n *\n * @param {Array|Map} modulesData Modules data to be used in the chooser.\n * @return {Promise} Promise resolved with the template data.\n */\n async getModChooserTemplateData(modulesData) {\n modulesData = this.normaliseModulesData(modulesData);\n const allStrings = await loadNecessaryStrings();\n const favouriteTab = await this.getFavouriteTabData(modulesData);\n\n const tabs = [\n {\n ...this.getTabData(\n 'all',\n modulesData,\n allStrings.all,\n null,\n !favouriteTab.displayed,\n ),\n hasSearchResults: true, // The all tab will also show search results.\n },\n favouriteTab,\n {\n ...this.getTabData(\n 'recommended',\n modulesData.filter(mod => mod.recommended === true),\n allStrings.recommended,\n allStrings.recommended_help\n ),\n separator: true, // Add a separator before the purpose categories.\n },\n ];\n\n activityCategories.forEach((category) => {\n const categoryModules = modulesData.filter(mod => mod.purpose == category);\n if (categoryModules.length === 0) {\n return;\n }\n tabs.push(\n this.getTabData(\n category,\n categoryModules,\n allStrings['mod_purpose_' + category],\n allStrings['mod_purpose_' + category + '_help']\n )\n );\n });\n\n return {\n modules: modulesData,\n tabs,\n };\n }\n\n /**\n * Get the module help template data.\n *\n * @param {Object} moduleData Data of the module to get help for.\n * @return {Promise} Promise resolved with the module help data.\n */\n async getModuleHelpTemplateData(moduleData) {\n const purposeKey = `mod_purpose_${moduleData.purpose}`;\n const allStrings = await loadNecessaryStrings();\n const data = {\n ...moduleData,\n };\n data.details = [\n {\n icon: 'i/categoryevent',\n label: allStrings['mod_purposes'],\n value: allStrings[purposeKey] || '',\n },\n ];\n return data;\n }\n\n /**\n * Get the favourite tab data.\n *\n * @param {Array|Map} modulesData Modules data to be used in the chooser.\n * @return {Promise} Promise resolved with the template data.\n */\n async getFavouriteTabData(modulesData) {\n modulesData = this.normaliseModulesData(modulesData);\n const allStrings = await loadNecessaryStrings();\n\n // We need to deconstruct the modules data to ensure it is an array.\n const favouriteModules = modulesData.filter(\n mod => {\n return mod.favourite === true;\n }\n );\n\n return this.getTabData(\n 'favourites',\n favouriteModules,\n allStrings.favourites,\n null,\n favouriteModules.length > 0,\n );\n }\n\n /**\n * Get the search result template data.\n *\n * @param {Array|Map} resultsModulesData Modules data to be used in the chooser.\n * @return {Object} The template data.\n */\n getSearchResultData(resultsModulesData) {\n resultsModulesData = this.normaliseModulesData(resultsModulesData);\n return {\n 'searchresultsnumber': resultsModulesData.length,\n 'searchresults': resultsModulesData,\n };\n }\n\n /**\n * Get the number of items in a tab.\n *\n * @param {TabData} tabData The tab data.\n * @return {Number} The number of items in the tab.\n */\n countTabItems(tabData) {\n return tabData.items?.length ?? 0;\n }\n\n /**\n * Get the activity chooser footer template data.\n * @param {Object} footerData The active footer data object.\n * @return {Object} The template data.\n */\n getFooterData(footerData) {\n return {\n 'activeFooter': footerData.customfootertemplate,\n };\n }\n}\n\n/**\n * Load the necessary strings for the activity chooser.\n *\n * @return {Promise} Promise resolved with the loaded strings.\n */\nasync function loadNecessaryStrings() {\n if (allStrings !== null) {\n return allStrings;\n }\n allStrings = {};\n\n const stringToLoad = [\n {key: 'all', component: 'core'},\n {key: 'favourites', component: 'core'},\n {key: 'recommended', component: 'core'},\n {key: 'recommended_help', component: 'core_course'},\n {key: 'mod_purposes', component: 'core_course'},\n ...activityCategories.map(\n (key) => ({\n key: 'mod_purpose_' + key,\n component: 'core_course',\n })\n ),\n ...activityCategories.map(\n (key) => ({\n key: 'mod_purpose_' + key + '_help',\n component: 'core_course',\n })\n ),\n ];\n\n const loadedStrings = await getStrings(stringToLoad);\n stringToLoad.forEach(({key}, index) => {\n allStrings[key] = loadedStrings[index];\n });\n return allStrings;\n}\n"],"names":["activityCategories","allStrings","loadNecessaryStrings","stringToLoad","key","component","map","loadedStrings","forEach","index","getTabData","tabId","filteredModules","tabLabel","tabHelp","result","active","items","displayed","length","normaliseModulesData","modulesData","Map","Array","from","values","isArray","Error","this","favouriteTab","getFavouriteTabData","tabs","all","hasSearchResults","filter","mod","recommended","recommended_help","separator","category","categoryModules","purpose","push","modules","moduleData","purposeKey","data","details","icon","label","value","favouriteModules","favourite","favourites","getSearchResultData","resultsModulesData","countTabItems","tabData","_tabData$items","getFooterData","footerData","customfootertemplate"],"mappings":";;;;;;;;MAyBMA,mBAAqB,CACvB,iBACA,aACA,gBACA,gBACA,UACA,0BAGAC,WAAa,KAEjBC,sCA0MeA,0BACQ,OAAfD,kBACOA,WAEXA,WAAa,SAEPE,aAAe,CACjB,CAACC,IAAK,MAAOC,UAAW,QACxB,CAACD,IAAK,aAAcC,UAAW,QAC/B,CAACD,IAAK,cAAeC,UAAW,QAChC,CAACD,IAAK,mBAAoBC,UAAW,eACrC,CAACD,IAAK,eAAgBC,UAAW,kBAC9BL,mBAAmBM,KACjBF,OACGA,IAAK,eAAiBA,IACtBC,UAAW,qBAGhBL,mBAAmBM,KACjBF,OACGA,IAAK,eAAiBA,IAAM,QAC5BC,UAAW,mBAKjBE,oBAAsB,mBAAWJ,qBACvCA,aAAaK,SAAQ,MAAQC,aAAPL,IAACA,UACnBH,WAAWG,KAAOG,cAAcE,UAE7BR,yCA9MPS,WAAWC,MAAOC,gBAAiBC,cAAUC,+DAAU,WAC7CC,OAAS,CACXJ,MAAOA,MACPK,+DACAC,MAAOL,gBACPM,UAAWN,gBAAgBO,OAAS,EACpCN,SAAAA,iBAEAC,UACAC,OAAOD,QAAUA,SAEdC,OAYXK,qBAAqBC,gBACbA,uBAAuBC,IACvBD,YAAcE,MAAMC,KAAKH,YAAYI,eAClC,IAAKF,MAAMG,QAAQL,mBAChB,IAAIM,MAAM,mEAEbN,4CASqBA,aAC5BA,YAAcO,KAAKR,qBAAqBC,mBAClCpB,iBAAmBC,uBACnB2B,mBAAqBD,KAAKE,oBAAoBT,aAE9CU,KAAO,CACT,IACOH,KAAKlB,WACJ,MACAW,YACApB,WAAW+B,IACX,MACCH,aAAaX,WAElBe,kBAAkB,GAEtBJ,aACA,IACOD,KAAKlB,WACJ,cACAW,YAAYa,QAAOC,MAA2B,IAApBA,IAAIC,cAC9BnC,WAAWmC,YACXnC,WAAWoC,kBAEfC,WAAW,WAInBtC,mBAAmBQ,SAAS+B,iBAClBC,gBAAkBnB,YAAYa,QAAOC,KAAOA,IAAIM,SAAWF,WAClC,IAA3BC,gBAAgBrB,QAGpBY,KAAKW,KACDd,KAAKlB,WACD6B,SACAC,gBACAvC,WAAW,eAAiBsC,UAC5BtC,WAAW,eAAiBsC,SAAW,cAK5C,CACHI,QAAStB,YACTU,KAAAA,sCAUwBa,kBACtBC,iCAA4BD,WAAWH,SACvCxC,iBAAmBC,uBACnB4C,KAAO,IACNF,mBAEPE,KAAKC,QAAU,CACX,CACIC,KAAM,kBACNC,MAAOhD,WAAU,aACjBiD,MAAOjD,WAAW4C,aAAe,KAGlCC,+BASezB,aACtBA,YAAcO,KAAKR,qBAAqBC,mBAClCpB,iBAAmBC,uBAGnBiD,iBAAmB9B,YAAYa,QACjCC,MAC6B,IAAlBA,IAAIiB,mBAIZxB,KAAKlB,WACR,aACAyC,iBACAlD,WAAWoD,WACX,KACAF,iBAAiBhC,OAAS,GAUlCmC,oBAAoBC,0BAET,sBADPA,mBAAqB3B,KAAKR,qBAAqBmC,qBAEDpC,qBACzBoC,oBAUzBC,cAAcC,8GACHA,QAAQxC,uCAARyC,eAAevC,8DAAU,EAQpCwC,cAAcC,kBACH,cACaA,WAAWC"} \ No newline at end of file +{"version":3,"file":"exporter.min.js","sources":["../../../src/local/activitychooser/exporter.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 * Module to generate template data for the activity chooser.\n *\n * @module core_course/local/activitychooser/exporter\n * @copyright 2025 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {getStrings} from 'core/str';\n\nconst activityCategories = [\n 'administration',\n 'assessment',\n 'collaboration',\n 'communication',\n 'content',\n 'interactivecontent',\n];\n\nlet allStrings = null;\n\nloadNecessaryStrings();\n\nexport default class {\n /**\n * A tab data structure.\n *\n * @typedef {object} TabData\n * @property {String} tabId the tab ID\n * @property {Boolean} active whether the tab is active or not\n * @property {Array} items the filtered modules to be displayed in the tab\n * @property {Boolean} displayed whether the tab is displayed or not\n * @property {String} tabLabel the tab label\n * @property {String|null} tabHelp the help text for the tab (optional)\n */\n\n /**\n * Generate a tab data object for the activity chooser.\n *\n * @private\n * @param {String} tabId Tab ID.\n * @param {Array} filteredModules Filtered modules to be displayed in the tab.\n * @param {String} tabLabel Tab label.\n * @param {String|null} tabHelp Help text for the tab (optional).\n * @param {Boolean} active Whether the tab is active or not.\n * @return {TabData} Tab data object.\n */\n getTabData(tabId, filteredModules, tabLabel, tabHelp = null, active = false) {\n const result = {\n tabId: tabId,\n active: active,\n items: filteredModules,\n displayed: filteredModules.length > 0,\n tabLabel,\n };\n if (tabHelp) {\n result.tabHelp = tabHelp;\n }\n return result;\n }\n\n /**\n * Normalise the modules data to be used in the chooser.\n *\n * The modulesData can be a plain array or a Map. This method will convert it to a\n * plain array of objects.\n *\n * @param {Array|Map} modulesData Modules data to be used in the chooser.\n * @return {Array} Normalised modules data.\n */\n normaliseModulesData(modulesData) {\n if (modulesData instanceof Map) {\n modulesData = Array.from(modulesData.values());\n } else if (!Array.isArray(modulesData)) {\n throw new Error('Invalid modules data format. Expected an array or a Map.');\n }\n return modulesData;\n }\n\n /**\n * Fetch the chooser template data for a specific section.\n *\n * @param {Array|Map} modulesData Modules data to be used in the chooser.\n * @return {Promise} Promise resolved with the template data.\n */\n async getModChooserTemplateData(modulesData) {\n modulesData = this.normaliseModulesData(modulesData);\n const allStrings = await loadNecessaryStrings();\n const favouriteTab = await this.getFavouriteTabData(modulesData);\n\n const tabs = [\n {\n ...this.getTabData(\n 'all',\n modulesData,\n allStrings.all,\n null,\n !favouriteTab.displayed,\n ),\n hasSearchResults: true, // The all tab will also show search results.\n },\n favouriteTab,\n {\n ...this.getTabData(\n 'recommended',\n modulesData.filter(mod => mod.recommended === true),\n allStrings.recommended,\n allStrings.recommended_help\n ),\n separator: true, // Add a separator before the purpose categories.\n },\n ];\n\n activityCategories.forEach((category) => {\n const categoryModules = modulesData.filter(mod => mod.purpose == category);\n if (categoryModules.length === 0) {\n return;\n }\n tabs.push(\n this.getTabData(\n category,\n categoryModules,\n allStrings['mod_purpose_' + category],\n allStrings['mod_purpose_' + category + '_help']\n )\n );\n });\n\n return {\n modules: modulesData,\n tabs,\n };\n }\n\n /**\n * Get the module help template data.\n *\n * @param {Object} moduleData Data of the module to get help for.\n * @return {Promise} Promise resolved with the module help data.\n */\n async getModuleHelpTemplateData(moduleData) {\n const purposeKey = `mod_purpose_${moduleData.purpose}`;\n const allStrings = await loadNecessaryStrings();\n const data = {\n ...moduleData,\n };\n data.details = [\n {\n label: allStrings['supports'],\n value: allStrings[purposeKey] || '',\n },\n ];\n return data;\n }\n\n /**\n * Get the favourite tab data.\n *\n * @param {Array|Map} modulesData Modules data to be used in the chooser.\n * @return {Promise} Promise resolved with the template data.\n */\n async getFavouriteTabData(modulesData) {\n modulesData = this.normaliseModulesData(modulesData);\n const allStrings = await loadNecessaryStrings();\n\n // We need to deconstruct the modules data to ensure it is an array.\n const favouriteModules = modulesData.filter(\n mod => {\n return mod.favourite === true;\n }\n );\n\n return this.getTabData(\n 'favourites',\n favouriteModules,\n allStrings.favourites,\n null,\n favouriteModules.length > 0,\n );\n }\n\n /**\n * Get the search result template data.\n *\n * @param {Array|Map} resultsModulesData Modules data to be used in the chooser.\n * @return {Object} The template data.\n */\n getSearchResultData(resultsModulesData) {\n resultsModulesData = this.normaliseModulesData(resultsModulesData);\n return {\n 'searchresultsnumber': resultsModulesData.length,\n 'searchresults': resultsModulesData,\n };\n }\n\n /**\n * Get the number of items in a tab.\n *\n * @param {TabData} tabData The tab data.\n * @return {Number} The number of items in the tab.\n */\n countTabItems(tabData) {\n return tabData.items?.length ?? 0;\n }\n\n /**\n * Get the activity chooser footer template data.\n * @param {Object} footerData The active footer data object.\n * @return {Object} The template data.\n */\n getFooterData(footerData) {\n return {\n 'activeFooter': footerData.customfootertemplate,\n };\n }\n}\n\n/**\n * Load the necessary strings for the activity chooser.\n *\n * @return {Promise} Promise resolved with the loaded strings.\n */\nasync function loadNecessaryStrings() {\n if (allStrings !== null) {\n return allStrings;\n }\n allStrings = {};\n\n const stringToLoad = [\n {key: 'all', component: 'core'},\n {key: 'favourites', component: 'core'},\n {key: 'recommended', component: 'core'},\n {key: 'recommended_help', component: 'core_course'},\n {key: 'supports', component: 'core_course'},\n ...activityCategories.map(\n (key) => ({\n key: 'mod_purpose_' + key,\n component: 'core_course',\n })\n ),\n ...activityCategories.map(\n (key) => ({\n key: 'mod_purpose_' + key + '_help',\n component: 'core_course',\n })\n ),\n ];\n\n const loadedStrings = await getStrings(stringToLoad);\n stringToLoad.forEach(({key}, index) => {\n allStrings[key] = loadedStrings[index];\n });\n return allStrings;\n}\n"],"names":["activityCategories","allStrings","loadNecessaryStrings","stringToLoad","key","component","map","loadedStrings","forEach","index","getTabData","tabId","filteredModules","tabLabel","tabHelp","result","active","items","displayed","length","normaliseModulesData","modulesData","Map","Array","from","values","isArray","Error","this","favouriteTab","getFavouriteTabData","tabs","all","hasSearchResults","filter","mod","recommended","recommended_help","separator","category","categoryModules","purpose","push","modules","moduleData","purposeKey","data","details","label","value","favouriteModules","favourite","favourites","getSearchResultData","resultsModulesData","countTabItems","tabData","_tabData$items","getFooterData","footerData","customfootertemplate"],"mappings":";;;;;;;;MAyBMA,mBAAqB,CACvB,iBACA,aACA,gBACA,gBACA,UACA,0BAGAC,WAAa,KAEjBC,sCAyMeA,0BACQ,OAAfD,kBACOA,WAEXA,WAAa,SAEPE,aAAe,CACjB,CAACC,IAAK,MAAOC,UAAW,QACxB,CAACD,IAAK,aAAcC,UAAW,QAC/B,CAACD,IAAK,cAAeC,UAAW,QAChC,CAACD,IAAK,mBAAoBC,UAAW,eACrC,CAACD,IAAK,WAAYC,UAAW,kBAC1BL,mBAAmBM,KACjBF,OACGA,IAAK,eAAiBA,IACtBC,UAAW,qBAGhBL,mBAAmBM,KACjBF,OACGA,IAAK,eAAiBA,IAAM,QAC5BC,UAAW,mBAKjBE,oBAAsB,mBAAWJ,qBACvCA,aAAaK,SAAQ,MAAQC,aAAPL,IAACA,UACnBH,WAAWG,KAAOG,cAAcE,UAE7BR,yCA7MPS,WAAWC,MAAOC,gBAAiBC,cAAUC,+DAAU,WAC7CC,OAAS,CACXJ,MAAOA,MACPK,+DACAC,MAAOL,gBACPM,UAAWN,gBAAgBO,OAAS,EACpCN,SAAAA,iBAEAC,UACAC,OAAOD,QAAUA,SAEdC,OAYXK,qBAAqBC,gBACbA,uBAAuBC,IACvBD,YAAcE,MAAMC,KAAKH,YAAYI,eAClC,IAAKF,MAAMG,QAAQL,mBAChB,IAAIM,MAAM,mEAEbN,4CASqBA,aAC5BA,YAAcO,KAAKR,qBAAqBC,mBAClCpB,iBAAmBC,uBACnB2B,mBAAqBD,KAAKE,oBAAoBT,aAE9CU,KAAO,CACT,IACOH,KAAKlB,WACJ,MACAW,YACApB,WAAW+B,IACX,MACCH,aAAaX,WAElBe,kBAAkB,GAEtBJ,aACA,IACOD,KAAKlB,WACJ,cACAW,YAAYa,QAAOC,MAA2B,IAApBA,IAAIC,cAC9BnC,WAAWmC,YACXnC,WAAWoC,kBAEfC,WAAW,WAInBtC,mBAAmBQ,SAAS+B,iBAClBC,gBAAkBnB,YAAYa,QAAOC,KAAOA,IAAIM,SAAWF,WAClC,IAA3BC,gBAAgBrB,QAGpBY,KAAKW,KACDd,KAAKlB,WACD6B,SACAC,gBACAvC,WAAW,eAAiBsC,UAC5BtC,WAAW,eAAiBsC,SAAW,cAK5C,CACHI,QAAStB,YACTU,KAAAA,sCAUwBa,kBACtBC,iCAA4BD,WAAWH,SACvCxC,iBAAmBC,uBACnB4C,KAAO,IACNF,mBAEPE,KAAKC,QAAU,CACX,CACIC,MAAO/C,WAAU,SACjBgD,MAAOhD,WAAW4C,aAAe,KAGlCC,+BASezB,aACtBA,YAAcO,KAAKR,qBAAqBC,mBAClCpB,iBAAmBC,uBAGnBgD,iBAAmB7B,YAAYa,QACjCC,MAC6B,IAAlBA,IAAIgB,mBAIZvB,KAAKlB,WACR,aACAwC,iBACAjD,WAAWmD,WACX,KACAF,iBAAiB/B,OAAS,GAUlCkC,oBAAoBC,0BAET,sBADPA,mBAAqB1B,KAAKR,qBAAqBkC,qBAEDnC,qBACzBmC,oBAUzBC,cAAcC,8GACHA,QAAQvC,uCAARwC,eAAetC,8DAAU,EAQpCuC,cAAcC,kBACH,cACaA,WAAWC"} \ No newline at end of file diff --git a/public/course/amd/src/local/activitychooser/dialogue.js b/public/course/amd/src/local/activitychooser/dialogue.js index f1cf1922563..1a8b5b7341b 100644 --- a/public/course/amd/src/local/activitychooser/dialogue.js +++ b/public/course/amd/src/local/activitychooser/dialogue.js @@ -285,6 +285,7 @@ class ActivityChooserDialogue { * @return {Promise} A promise that resolves when the event is handled */ async handleModalClick(event) { + event.preventDefault(); const target = event.target; if (target.closest(selectors.actions.optionActions.showSummary)) { @@ -309,7 +310,7 @@ class ActivityChooserDialogue { return; } - if (target.closest(selectors.regions.chooserOption.container)) { + if (target.closest(selectors.regions.chooserOption.info)) { event.preventDefault(); this.handleOptionSelection(target); return; diff --git a/public/course/amd/src/local/activitychooser/dialoguedom.js b/public/course/amd/src/local/activitychooser/dialoguedom.js index 78e06ebe5e3..99008fb516d 100644 --- a/public/course/amd/src/local/activitychooser/dialoguedom.js +++ b/public/course/amd/src/local/activitychooser/dialoguedom.js @@ -180,8 +180,6 @@ export default class ChooserDialogueDOM { `${selectors.elements.moduleItem(internal)} ${selectors.actions.optionActions.manageFavourite}` ); Array.from(favouriteButtons).forEach((element) => { - element.classList.toggle('text-muted', !favourite); - element.classList.toggle('text-primary', favourite); element.dataset.favourited = favourite; element.setAttribute('aria-pressed', favourite); element.querySelector(selectors.elements.favouriteIconActive)?.classList.toggle('d-none', !favourite); @@ -190,6 +188,7 @@ export default class ChooserDialogueDOM { const iconSelectsor = favourite ? selectors.elements.favouriteIconActive : selectors.elements.favouriteIconInactive; const favouriteIcon = element.querySelector(iconSelectsor); element.setAttribute('aria-label', favouriteIcon?.getAttribute('data-action-label') || ''); + element.setAttribute('title', favouriteIcon?.getAttribute('data-action-label') || ''); }); } @@ -543,7 +542,6 @@ export default class ChooserDialogueDOM { markChooserOptionAsSelected(chooserOption) { this.unmarkAllChooserOptionAsSelected(); chooserOption.classList.add('selected'); - chooserOption.classList.add('border-primary'); chooserOption.setAttribute('aria-selected', 'true'); const footer = getFirst(this.modal.getFooter()); @@ -560,7 +558,6 @@ export default class ChooserDialogueDOM { const selectedOptions = this.modalBody.querySelectorAll(`${selectors.regions.chooserOption.container}.selected`); selectedOptions.forEach((option) => { option.classList.remove('selected'); - option.classList.remove('border-primary'); option.setAttribute('aria-selected', 'false'); }); diff --git a/public/course/amd/src/local/activitychooser/exporter.js b/public/course/amd/src/local/activitychooser/exporter.js index cdbbc952f52..60b3ce65112 100644 --- a/public/course/amd/src/local/activitychooser/exporter.js +++ b/public/course/amd/src/local/activitychooser/exporter.js @@ -161,8 +161,7 @@ export default class { }; data.details = [ { - icon: 'i/categoryevent', - label: allStrings['mod_purposes'], + label: allStrings['supports'], value: allStrings[purposeKey] || '', }, ]; @@ -247,7 +246,7 @@ async function loadNecessaryStrings() { {key: 'favourites', component: 'core'}, {key: 'recommended', component: 'core'}, {key: 'recommended_help', component: 'core_course'}, - {key: 'mod_purposes', component: 'core_course'}, + {key: 'supports', component: 'core_course'}, ...activityCategories.map( (key) => ({ key: 'mod_purpose_' + key, diff --git a/public/course/templates/activitychooser.mustache b/public/course/templates/activitychooser.mustache index d901b185f16..7ca20da6c91 100644 --- a/public/course/templates/activitychooser.mustache +++ b/public/course/templates/activitychooser.mustache @@ -45,11 +45,11 @@