MDL-85264 output: fix form change checker when switching tabs.
During the Bootstrap 5.0 upgrade the event handling within dynamic
tabs appears to have changed such that the "click" event happens
too late for the form change checker module to determine whether
there were any "dirty" forms on the tab being switched from.
This is related to, although not specifically caused by, changes
made in 0c586b41.
This commit is contained in:
Vendored
+1
-1
@@ -5,6 +5,6 @@ define("core/dynamic_tabs",["exports","jquery","core/templates","core/loadingico
|
||||
* @module core/dynamic_tabs
|
||||
* @copyright 2021 David Matamoros <davidmc@moodle.com> based on code from Marina Glancy
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_jquery=_interopRequireDefault(_jquery),_templates=_interopRequireDefault(_templates),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending);const SELECTORS={dynamicTabs:".dynamictabs",activeTab:".dynamictabs .nav-link.active",allActiveTabs:'.dynamictabs .nav-link[data-bs-toggle="tab"]:not(.disabled)',tabContent:".dynamictabs .tab-pane [data-tab-content]",tabToggle:'a[data-bs-toggle="tab"]',tabPane:".dynamictabs .tab-pane",forTabName:tabName=>'.dynamictabs [data-tab-content="'.concat(tabName,'"]'),forTabId:tabName=>'.dynamictabs [data-bs-toggle="tab"][href="#'.concat(tabName,'"]')};_exports.init=()=>{if(document.querySelectorAll(SELECTORS.tabToggle).forEach((tabToggle=>{tabToggle.addEventListener("click",(event=>{(0,_changechecker.isAnyWatchedFormDirty)()&&(event.preventDefault(),event.stopPropagation(),(0,_str.getStrings)([{key:"changesmade",component:"moodle"},{key:"changesmadereallygoaway",component:"moodle"},{key:"confirm",component:"moodle"}]).then((_ref=>{let[strChangesMade,strChangesMadeReally,strConfirm]=_ref;return _notification.default.confirm(strChangesMade,strChangesMadeReally,strConfirm,null,(()=>{(0,_changechecker.resetAllFormDirtyStates)(),(0,_jquery.default)(event.target).trigger(event.type)}))})).catch(_notification.default.exception))})),tabToggle.addEventListener("show.bs.tab",(()=>{const previousTabName=getActiveTabName();if(previousTabName){document.querySelector(SELECTORS.forTabName(previousTabName)).textContent=""}})),tabToggle.addEventListener("shown.bs.tab",(()=>{const tabPane=document.getElementById(tabToggle.getAttribute("href").replace(/^#/,""));tabPane&&loadTab(tabPane.id)}))})),!openTabFromHash()){const tabs=document.querySelector(SELECTORS.allActiveTabs);if(tabs)openTab(tabs.getAttribute("aria-controls"));else{const tabPane=document.querySelector(SELECTORS.tabPane);tabPane&&(tabPane.classList.add("active","show"),loadTab(tabPane.getAttribute("id")))}}};const getActiveTabName=()=>{const element=document.querySelector(SELECTORS.activeTab);return(null==element?void 0:element.getAttribute("aria-controls"))||null},loadTab=tabName=>{var _ref2,_tabName;tabName=null!==(_ref2=null!==(_tabName=tabName)&&void 0!==_tabName?_tabName:getActiveTabName())&&void 0!==_ref2?_ref2:(()=>{const element=document.querySelector(SELECTORS.tabContent);return(null==element?void 0:element.dataset.tabContent)||null})();const tab=document.querySelector(SELECTORS.forTabName(tabName));if(!tab)return;const pendingPromise=new _pending.default("core/dynamic_tabs:loadTab:"+tabName);(0,_loadingicon.addIconToContainer)(tab).then((()=>{let tabArgs={...tab.dataset};return delete tabArgs.tabClass,delete tabArgs.tabContent,(0,_dynamic_tabs.getContent)(tab.dataset.tabClass,JSON.stringify(tabArgs))})).then((response=>Promise.all([_jquery.default.parseHTML(response.javascript,null,!0).map((node=>node.innerHTML)).join("\n"),_templates.default.renderForPromise(response.template,JSON.parse(response.content))]))).then((_ref3=>{let[responseJs,{html:html,js:js}]=_ref3;return _templates.default.replaceNodeContents(tab,html,js+responseJs)})).then((()=>pendingPromise.resolve())).catch(_notification.default.exception)},openTab=tabName=>{const tab=(tabName=>document.querySelector(SELECTORS.forTabId(tabName)))(tabName);return!!tab&&(loadTab(tabName),tab.classList.add("active"),(tabName=>document.getElementById(tabName))(tabName).classList.add("active","show"),!0)},openTabFromHash=()=>{const hash=document.location.hash;return!!hash.match(/^#\w+$/g)&&openTab(hash.replace(/^#/g,""))}}));
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_jquery=_interopRequireDefault(_jquery),_templates=_interopRequireDefault(_templates),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending);const SELECTORS={dynamicTabs:".dynamictabs",activeTab:".dynamictabs .nav-link.active",allActiveTabs:'.dynamictabs .nav-link[data-bs-toggle="tab"]:not(.disabled)',tabContent:".dynamictabs .tab-pane [data-tab-content]",tabToggle:'a[data-bs-toggle="tab"]',tabPane:".dynamictabs .tab-pane",forTabName:tabName=>'.dynamictabs [data-tab-content="'.concat(tabName,'"]'),forTabId:tabName=>'.dynamictabs [data-bs-toggle="tab"][href="#'.concat(tabName,'"]')};let watchedFormDirtyNotification=!1;_exports.init=()=>{if(document.querySelectorAll(SELECTORS.tabToggle).forEach((tabToggle=>{tabToggle.addEventListener("show.bs.tab",(event=>{if((0,_changechecker.isAnyWatchedFormDirty)())return event.preventDefault(),event.stopPropagation(),void(watchedFormDirtyNotification||(watchedFormDirtyNotification=!0,_notification.default.saveCancelPromise((0,_str.getString)("changesmade"),(0,_str.getString)("changesmadereallygoaway"),(0,_str.getString)("confirm"),{triggerElement:tabToggle}).then((()=>{(0,_changechecker.resetAllFormDirtyStates)(),tabToggle.dispatchEvent(new Event("click",{bubbles:!0}))})).catch((()=>{})).finally((()=>{watchedFormDirtyNotification=!1}))));const previousTabName=getActiveTabName();if(previousTabName){document.querySelector(SELECTORS.forTabName(previousTabName)).textContent=""}})),tabToggle.addEventListener("shown.bs.tab",(()=>{const tabPane=document.getElementById(tabToggle.getAttribute("href").replace(/^#/,""));tabPane&&loadTab(tabPane.id)}))})),!openTabFromHash()){const tabs=document.querySelector(SELECTORS.allActiveTabs);if(tabs)openTab(tabs.getAttribute("aria-controls"));else{const tabPane=document.querySelector(SELECTORS.tabPane);tabPane&&(tabPane.classList.add("active","show"),loadTab(tabPane.getAttribute("id")))}}};const getActiveTabName=()=>{const element=document.querySelector(SELECTORS.activeTab);return(null==element?void 0:element.getAttribute("aria-controls"))||null},loadTab=tabName=>{var _ref,_tabName;tabName=null!==(_ref=null!==(_tabName=tabName)&&void 0!==_tabName?_tabName:getActiveTabName())&&void 0!==_ref?_ref:(()=>{const element=document.querySelector(SELECTORS.tabContent);return(null==element?void 0:element.dataset.tabContent)||null})();const tab=document.querySelector(SELECTORS.forTabName(tabName));if(!tab)return;const pendingPromise=new _pending.default("core/dynamic_tabs:loadTab:"+tabName);(0,_loadingicon.addIconToContainer)(tab).then((()=>{let tabArgs={...tab.dataset};return delete tabArgs.tabClass,delete tabArgs.tabContent,(0,_dynamic_tabs.getContent)(tab.dataset.tabClass,JSON.stringify(tabArgs))})).then((response=>Promise.all([_jquery.default.parseHTML(response.javascript,null,!0).map((node=>node.innerHTML)).join("\n"),_templates.default.renderForPromise(response.template,JSON.parse(response.content))]))).then((_ref2=>{let[responseJs,{html:html,js:js}]=_ref2;return _templates.default.replaceNodeContents(tab,html,js+responseJs)})).then((()=>pendingPromise.resolve())).catch(_notification.default.exception)},openTab=tabName=>{const tab=(tabName=>document.querySelector(SELECTORS.forTabId(tabName)))(tabName);return!!tab&&(loadTab(tabName),tab.classList.add("active"),(tabName=>document.getElementById(tabName))(tabName).classList.add("active","show"),!0)},openTabFromHash=()=>{const hash=document.location.hash;return!!hash.match(/^#\w+$/g)&&openTab(hash.replace(/^#/g,""))}}));
|
||||
|
||||
//# sourceMappingURL=dynamic_tabs.min.js.map
|
||||
File diff suppressed because one or more lines are too long
+29
-21
@@ -26,7 +26,7 @@ import Templates from 'core/templates';
|
||||
import {addIconToContainer} from 'core/loadingicon';
|
||||
import Notification from 'core/notification';
|
||||
import Pending from 'core/pending';
|
||||
import {getStrings} from 'core/str';
|
||||
import {getString} from 'core/str';
|
||||
import {getContent} from 'core/local/repository/dynamic_tabs';
|
||||
import {isAnyWatchedFormDirty, resetAllFormDirtyStates} from 'core_form/changechecker';
|
||||
|
||||
@@ -42,36 +42,44 @@ const SELECTORS = {
|
||||
SELECTORS.forTabName = tabName => `.dynamictabs [data-tab-content="${tabName}"]`;
|
||||
SELECTORS.forTabId = tabName => `.dynamictabs [data-bs-toggle="tab"][href="#${tabName}"]`;
|
||||
|
||||
let watchedFormDirtyNotification = false;
|
||||
|
||||
/**
|
||||
* Initialises the tabs view on the page (only one tabs view per page is supported)
|
||||
*/
|
||||
export const init = () => {
|
||||
const tabToggles = document.querySelectorAll(SELECTORS.tabToggle);
|
||||
|
||||
tabToggles.forEach(tabToggle => {
|
||||
// Listen to click, warn user if they are navigating away with unsaved form changes.
|
||||
tabToggle.addEventListener('click', (event) => {
|
||||
if (!isAnyWatchedFormDirty()) {
|
||||
tabToggle.addEventListener('show.bs.tab', (event) => {
|
||||
if (isAnyWatchedFormDirty()) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
// Prevent double execution of event listener.
|
||||
if (!watchedFormDirtyNotification) {
|
||||
watchedFormDirtyNotification = true;
|
||||
|
||||
Notification.saveCancelPromise(
|
||||
getString('changesmade'),
|
||||
getString('changesmadereallygoaway'),
|
||||
getString('confirm'),
|
||||
{triggerElement: tabToggle}
|
||||
).then(() => {
|
||||
// Reset form dirty state on confirmation, re-trigger the event.
|
||||
resetAllFormDirtyStates();
|
||||
tabToggle.dispatchEvent(new Event('click', {bubbles: true}));
|
||||
return;
|
||||
}).catch(() => {
|
||||
// User cancelled the dialogue.
|
||||
}).finally(() => {
|
||||
watchedFormDirtyNotification = false;
|
||||
});
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
getStrings([
|
||||
{key: 'changesmade', component: 'moodle'},
|
||||
{key: 'changesmadereallygoaway', component: 'moodle'},
|
||||
{key: 'confirm', component: 'moodle'},
|
||||
]).then(([strChangesMade, strChangesMadeReally, strConfirm]) =>
|
||||
// Reset form dirty state on confirmation, re-trigger the event.
|
||||
Notification.confirm(strChangesMade, strChangesMadeReally, strConfirm, null, () => {
|
||||
resetAllFormDirtyStates();
|
||||
$(event.target).trigger(event.type);
|
||||
})
|
||||
).catch(Notification.exception);
|
||||
});
|
||||
|
||||
tabToggle.addEventListener('show.bs.tab', () => {
|
||||
// Clean content from previous tab.
|
||||
const previousTabName = getActiveTabName();
|
||||
if (previousTabName) {
|
||||
|
||||
Reference in New Issue
Block a user