From 08d68341661ea16e36b619b7a87bb6e3f13c44f7 Mon Sep 17 00:00:00 2001 From: Paul Holden Date: Tue, 16 Dec 2025 13:10:21 +0000 Subject: [PATCH] MDL-85300 output: ensure Bootstrap tooltips load for dynamic content. Prior to this change, tooltips would only load for elements in the initial page DOM without considering those elements subsequently added dynamically (e.g. via AJAX). Co-authored-by: Sebastian Gundersen --- public/theme/boost/amd/build/loader.min.js | 4 ++-- .../theme/boost/amd/build/loader.min.js.map | 2 +- public/theme/boost/amd/src/loader.js | 20 +++++++++++++++++-- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/public/theme/boost/amd/build/loader.min.js b/public/theme/boost/amd/build/loader.min.js index 818151b1b1e..23df96fb06d 100644 --- a/public/theme/boost/amd/build/loader.min.js +++ b/public/theme/boost/amd/build/loader.min.js @@ -1,4 +1,4 @@ -define("theme_boost/loader",["exports","./aria","./index","core/pending","./bootstrap/util/sanitizer","./pending","./bootstrap/dom/event-handler"],(function(_exports,Aria,Bootstrap,_pending,_sanitizer,_pending2,_eventHandler){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj} +define("theme_boost/loader",["exports","./aria","./index","core/pending","core_filters/events","./bootstrap/util/sanitizer","./pending","./bootstrap/dom/event-handler"],(function(_exports,Aria,Bootstrap,_pending,_events,_sanitizer,_pending2,_eventHandler){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj} /** * Template renderer for Moodle. Load and render Moodle templates with Mustache. * @@ -6,6 +6,6 @@ define("theme_boost/loader",["exports","./aria","./index","core/pending","./boot * @copyright 2015 Damyon Wiese * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since 2.9 - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.Bootstrap=void 0,Aria=_interopRequireWildcard(Aria),Bootstrap=_interopRequireWildcard(Bootstrap),_exports.Bootstrap=Bootstrap,_pending=_interopRequireDefault(_pending),_pending2=_interopRequireDefault(_pending2),_eventHandler=_interopRequireDefault(_eventHandler);const pendingPromise=new _pending.default("theme_boost/loader:init");(0,_pending2.default)(),Aria.init(),(()=>{[...document.querySelectorAll('a[data-bs-toggle="tab"]')].map((tabTriggerEl=>tabTriggerEl.addEventListener("shown.bs.tab",(e=>{var hash=e.target.getAttribute("href");history.replaceState?history.replaceState(null,null,hash):location.hash=hash}))));const hash=window.location.hash;if(hash){const tab=document.querySelector('[role="tablist"] [href="'+hash+'"]');tab&&tab.click()}})(),(()=>{const popoverTriggerList=document.querySelectorAll('[data-bs-toggle="popover"]'),popoverConfig={container:"body",trigger:"focus",allowList:Object.assign(_sanitizer.DefaultAllowlist,{table:[],thead:[],tbody:[],tr:[],th:[],td:[]})};[...popoverTriggerList].map((popoverTriggerEl=>new Bootstrap.Popover(popoverTriggerEl,popoverConfig))),document.addEventListener("core/modal:bodyRendered",(e=>{[...e.target.querySelectorAll('[data-bs-toggle="popover"]')].map((popoverTriggerEl=>new Bootstrap.Popover(popoverTriggerEl,popoverConfig)))})),document.addEventListener("keydown",(e=>{const popoverTrigger=e.target.closest('[data-bs-toggle="popover"]');"Escape"===e.key&&popoverTrigger&&Bootstrap.Popover.getOrCreateInstance(popoverTrigger).hide(),"Enter"===e.key&&popoverTrigger&&Bootstrap.Popover.getOrCreateInstance(popoverTrigger).show()})),document.addEventListener("click",(e=>{const popoverTrigger=e.target.closest('[data-bs-toggle="popover"]');if(!popoverTrigger)return;const popover=Bootstrap.Popover.getOrCreateInstance(popoverTrigger);popover._isShown()||popover.show()}))})(),(()=>{const tooltipList=[...document.querySelectorAll('[data-bs-toggle="tooltip"]')].map((tooltipTriggerEl=>new Bootstrap.Tooltip(tooltipTriggerEl)));document.addEventListener("keydown",(e=>{"Escape"===e.key&&tooltipList.forEach((tooltip=>{tooltip.hide()}))}))})(),_eventHandler.default.off(document,"keydown.bs.dropdown.data-api",".dropdown-menu",Bootstrap.Dropdown.dataApiKeydownHandler),_eventHandler.default.on(document.body,"keydown.bs.dropdown.data-api",".dropdown-menu",Bootstrap.Dropdown.dataApiKeydownHandler),pendingPromise.resolve()})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.Bootstrap=void 0,Aria=_interopRequireWildcard(Aria),Bootstrap=_interopRequireWildcard(Bootstrap),_exports.Bootstrap=Bootstrap,_pending=_interopRequireDefault(_pending),_pending2=_interopRequireDefault(_pending2),_eventHandler=_interopRequireDefault(_eventHandler);const enableTooltips=function(){let rootElement=arguments.length>0&&void 0!==arguments[0]?arguments[0]:document;const tooltipTriggerList=rootElement.querySelectorAll('[data-bs-toggle="tooltip"]'),tooltipList=[...tooltipTriggerList].map((tooltipTriggerEl=>new Bootstrap.Tooltip(tooltipTriggerEl)));document.addEventListener("keydown",(e=>{"Escape"===e.key&&tooltipList.forEach((tooltip=>{tooltip.hide()}))}))},pendingPromise=new _pending.default("theme_boost/loader:init");(0,_pending2.default)(),Aria.init(),(()=>{[...document.querySelectorAll('a[data-bs-toggle="tab"]')].map((tabTriggerEl=>tabTriggerEl.addEventListener("shown.bs.tab",(e=>{var hash=e.target.getAttribute("href");history.replaceState?history.replaceState(null,null,hash):location.hash=hash}))));const hash=window.location.hash;if(hash){const tab=document.querySelector('[role="tablist"] [href="'+hash+'"]');tab&&tab.click()}})(),(()=>{const popoverTriggerList=document.querySelectorAll('[data-bs-toggle="popover"]'),popoverConfig={container:"body",trigger:"focus",allowList:Object.assign(_sanitizer.DefaultAllowlist,{table:[],thead:[],tbody:[],tr:[],th:[],td:[]})};[...popoverTriggerList].map((popoverTriggerEl=>new Bootstrap.Popover(popoverTriggerEl,popoverConfig))),document.addEventListener("core/modal:bodyRendered",(e=>{[...e.target.querySelectorAll('[data-bs-toggle="popover"]')].map((popoverTriggerEl=>new Bootstrap.Popover(popoverTriggerEl,popoverConfig)))})),document.addEventListener("keydown",(e=>{const popoverTrigger=e.target.closest('[data-bs-toggle="popover"]');"Escape"===e.key&&popoverTrigger&&Bootstrap.Popover.getOrCreateInstance(popoverTrigger).hide(),"Enter"===e.key&&popoverTrigger&&Bootstrap.Popover.getOrCreateInstance(popoverTrigger).show()})),document.addEventListener("click",(e=>{const popoverTrigger=e.target.closest('[data-bs-toggle="popover"]');if(!popoverTrigger)return;const popover=Bootstrap.Popover.getOrCreateInstance(popoverTrigger);popover._isShown()||popover.show()}))})(),enableTooltips(),document.addEventListener(_events.eventTypes.filterContentUpdated,(e=>{e.detail.nodes.forEach((node=>{node instanceof HTMLElement&&enableTooltips(node)}))})),_eventHandler.default.off(document,"keydown.bs.dropdown.data-api",".dropdown-menu",Bootstrap.Dropdown.dataApiKeydownHandler),_eventHandler.default.on(document.body,"keydown.bs.dropdown.data-api",".dropdown-menu",Bootstrap.Dropdown.dataApiKeydownHandler),pendingPromise.resolve()})); //# sourceMappingURL=loader.min.js.map \ No newline at end of file diff --git a/public/theme/boost/amd/build/loader.min.js.map b/public/theme/boost/amd/build/loader.min.js.map index eedb6585e66..508a690c2db 100644 --- a/public/theme/boost/amd/build/loader.min.js.map +++ b/public/theme/boost/amd/build/loader.min.js.map @@ -1 +1 @@ -{"version":3,"file":"loader.min.js","sources":["../src/loader.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 * Template renderer for Moodle. Load and render Moodle templates with Mustache.\n *\n * @module theme_boost/loader\n * @copyright 2015 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 2.9\n */\n\nimport * as Aria from './aria';\nimport * as Bootstrap from './index';\nimport Pending from 'core/pending';\nimport {DefaultAllowlist} from './bootstrap/util/sanitizer';\nimport setupBootstrapPendingChecks from './pending';\nimport EventHandler from './bootstrap/dom/event-handler';\n\n/**\n * Rember the last visited tabs.\n */\nconst rememberTabs = () => {\n const tabTriggerList = document.querySelectorAll('a[data-bs-toggle=\"tab\"]');\n [...tabTriggerList].map(tabTriggerEl => tabTriggerEl.addEventListener('shown.bs.tab', (e) => {\n var hash = e.target.getAttribute('href');\n if (history.replaceState) {\n history.replaceState(null, null, hash);\n } else {\n location.hash = hash;\n }\n }));\n const hash = window.location.hash;\n if (hash) {\n const tab = document.querySelector('[role=\"tablist\"] [href=\"' + hash + '\"]');\n if (tab) {\n tab.click();\n }\n }\n};\n\n/**\n * Enable all popovers\n *\n */\nconst enablePopovers = () => {\n const popoverTriggerList = document.querySelectorAll('[data-bs-toggle=\"popover\"]');\n const popoverConfig = {\n container: 'body',\n trigger: 'focus',\n allowList: Object.assign(DefaultAllowlist, {table: [], thead: [], tbody: [], tr: [], th: [], td: []}),\n };\n [...popoverTriggerList].map(popoverTriggerEl => new Bootstrap.Popover(popoverTriggerEl, popoverConfig));\n\n // Enable dynamically created popovers inside modals.\n document.addEventListener('core/modal:bodyRendered', (e) => {\n const modal = e.target;\n const popoverTriggerList = modal.querySelectorAll('[data-bs-toggle=\"popover\"]');\n [...popoverTriggerList].map(popoverTriggerEl => new Bootstrap.Popover(popoverTriggerEl, popoverConfig));\n });\n\n document.addEventListener('keydown', e => {\n const popoverTrigger = e.target.closest('[data-bs-toggle=\"popover\"]');\n if (e.key === 'Escape' && popoverTrigger) {\n Bootstrap.Popover.getOrCreateInstance(popoverTrigger).hide();\n }\n if (e.key === 'Enter' && popoverTrigger) {\n Bootstrap.Popover.getOrCreateInstance(popoverTrigger).show();\n }\n });\n document.addEventListener('click', e => {\n const popoverTrigger = e.target.closest('[data-bs-toggle=\"popover\"]');\n if (!popoverTrigger) {\n return;\n }\n const popover = Bootstrap.Popover.getOrCreateInstance(popoverTrigger);\n if (!popover._isShown()) {\n popover.show();\n }\n });\n};\n\n/**\n * Enable tooltips\n *\n */\nconst enableTooltips = () => {\n const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle=\"tooltip\"]');\n const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new Bootstrap.Tooltip(tooltipTriggerEl));\n\n document.addEventListener('keydown', e => {\n if (e.key === 'Escape') {\n tooltipList.forEach(tooltip => {\n tooltip.hide();\n });\n }\n });\n};\n\n/**\n * Realocate Bootstrap events to the body element.\n *\n * Bootstrap 5 has a unique event handling mechanism that attaches all event handlers at the document level\n * during the capture phase, rather than the usual bubbling phase. As a result, original Bootstrap events\n * cannot be stopped or prevented, since the document is the first node executed in the capture phase.\n * For certain advanced UI elements, such as form autocomplete, it is important to capture key-down events before\n * Bootstrap's handlers to prevent unintended closures of elements. Therefore, we need to change the Bootstrap handler\n * so that it operates one level lower, specifically at the body level.\n */\nconst realocateBootstrapEvents = () => {\n EventHandler.off(document, 'keydown.bs.dropdown.data-api', '.dropdown-menu', Bootstrap.Dropdown.dataApiKeydownHandler);\n EventHandler.on(document.body, 'keydown.bs.dropdown.data-api', '.dropdown-menu', Bootstrap.Dropdown.dataApiKeydownHandler);\n};\n\nconst pendingPromise = new Pending('theme_boost/loader:init');\n\n// Add pending promise event listeners to relevant Bootstrap custom events.\nsetupBootstrapPendingChecks();\n\n// Setup Aria helpers for Bootstrap features.\nAria.init();\n\n// Remember the last visited tabs.\nrememberTabs();\n\n// Enable all popovers.\nenablePopovers();\n\n// Enable all tooltips.\nenableTooltips();\n\n// Realocate Bootstrap events to the body element.\nrealocateBootstrapEvents();\n\npendingPromise.resolve();\n\nexport {\n Bootstrap,\n};\n"],"names":["pendingPromise","Pending","Aria","init","document","querySelectorAll","map","tabTriggerEl","addEventListener","e","hash","target","getAttribute","history","replaceState","location","window","tab","querySelector","click","rememberTabs","popoverTriggerList","popoverConfig","container","trigger","allowList","Object","assign","DefaultAllowlist","table","thead","tbody","tr","th","td","popoverTriggerEl","Bootstrap","Popover","popoverTrigger","closest","key","getOrCreateInstance","hide","show","popover","_isShown","enablePopovers","tooltipList","tooltipTriggerEl","Tooltip","forEach","tooltip","enableTooltips","off","Dropdown","dataApiKeydownHandler","on","body","resolve"],"mappings":";;;;;;;;oVA8HMA,eAAiB,IAAIC,iBAAQ,mDAMnCC,KAAKC,OAlGgB,UACMC,SAASC,iBAAiB,4BAC7BC,KAAIC,cAAgBA,aAAaC,iBAAiB,gBAAiBC,QAC/EC,KAAOD,EAAEE,OAAOC,aAAa,QAC7BC,QAAQC,aACRD,QAAQC,aAAa,KAAM,KAAMJ,MAEjCK,SAASL,KAAOA,gBAGlBA,KAAOM,OAAOD,SAASL,QACzBA,KAAM,OACAO,IAAMb,SAASc,cAAc,2BAA6BR,KAAO,MACnEO,KACAA,IAAIE,UAuFhBC,GA9EuB,YACbC,mBAAqBjB,SAASC,iBAAiB,8BAC/CiB,cAAgB,CAClBC,UAAW,OACXC,QAAS,QACTC,UAAWC,OAAOC,OAAOC,4BAAkB,CAACC,MAAO,GAAIC,MAAO,GAAIC,MAAO,GAAIC,GAAI,GAAIC,GAAI,GAAIC,GAAI,UAEjGb,oBAAoBf,KAAI6B,kBAAoB,IAAIC,UAAUC,QAAQF,iBAAkBb,iBAGxFlB,SAASI,iBAAiB,2BAA4BC,QACpCA,EAAEE,OACiBN,iBAAiB,+BAC1BC,KAAI6B,kBAAoB,IAAIC,UAAUC,QAAQF,iBAAkBb,oBAG5FlB,SAASI,iBAAiB,WAAWC,UAC3B6B,eAAiB7B,EAAEE,OAAO4B,QAAQ,8BAC1B,WAAV9B,EAAE+B,KAAoBF,gBACtBF,UAAUC,QAAQI,oBAAoBH,gBAAgBI,OAE5C,UAAVjC,EAAE+B,KAAmBF,gBACrBF,UAAUC,QAAQI,oBAAoBH,gBAAgBK,UAG9DvC,SAASI,iBAAiB,SAASC,UACzB6B,eAAiB7B,EAAEE,OAAO4B,QAAQ,kCACnCD,4BAGCM,QAAUR,UAAUC,QAAQI,oBAAoBH,gBACjDM,QAAQC,YACTD,QAAQD,WAiDpBG,GAxCuB,YAEbC,YAAc,IADO3C,SAASC,iBAAiB,+BACTC,KAAI0C,kBAAoB,IAAIZ,UAAUa,QAAQD,oBAE1F5C,SAASI,iBAAiB,WAAWC,IACnB,WAAVA,EAAE+B,KACFO,YAAYG,SAAQC,UAChBA,QAAQT,cAoCxBU,yBAnBiBC,IAAIjD,SAAU,+BAAgC,iBAAkBgC,UAAUkB,SAASC,6CACnFC,GAAGpD,SAASqD,KAAM,+BAAgC,iBAAkBrB,UAAUkB,SAASC,uBAuBxGvD,eAAe0D"} \ No newline at end of file +{"version":3,"file":"loader.min.js","sources":["../src/loader.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 * Template renderer for Moodle. Load and render Moodle templates with Mustache.\n *\n * @module theme_boost/loader\n * @copyright 2015 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 2.9\n */\n\nimport * as Aria from './aria';\nimport * as Bootstrap from './index';\nimport Pending from 'core/pending';\nimport {eventTypes} from 'core_filters/events';\nimport {DefaultAllowlist} from './bootstrap/util/sanitizer';\nimport setupBootstrapPendingChecks from './pending';\nimport EventHandler from './bootstrap/dom/event-handler';\n\n/**\n * Rember the last visited tabs.\n */\nconst rememberTabs = () => {\n const tabTriggerList = document.querySelectorAll('a[data-bs-toggle=\"tab\"]');\n [...tabTriggerList].map(tabTriggerEl => tabTriggerEl.addEventListener('shown.bs.tab', (e) => {\n var hash = e.target.getAttribute('href');\n if (history.replaceState) {\n history.replaceState(null, null, hash);\n } else {\n location.hash = hash;\n }\n }));\n const hash = window.location.hash;\n if (hash) {\n const tab = document.querySelector('[role=\"tablist\"] [href=\"' + hash + '\"]');\n if (tab) {\n tab.click();\n }\n }\n};\n\n/**\n * Enable all popovers\n *\n */\nconst enablePopovers = () => {\n const popoverTriggerList = document.querySelectorAll('[data-bs-toggle=\"popover\"]');\n const popoverConfig = {\n container: 'body',\n trigger: 'focus',\n allowList: Object.assign(DefaultAllowlist, {table: [], thead: [], tbody: [], tr: [], th: [], td: []}),\n };\n [...popoverTriggerList].map(popoverTriggerEl => new Bootstrap.Popover(popoverTriggerEl, popoverConfig));\n\n // Enable dynamically created popovers inside modals.\n document.addEventListener('core/modal:bodyRendered', (e) => {\n const modal = e.target;\n const popoverTriggerList = modal.querySelectorAll('[data-bs-toggle=\"popover\"]');\n [...popoverTriggerList].map(popoverTriggerEl => new Bootstrap.Popover(popoverTriggerEl, popoverConfig));\n });\n\n document.addEventListener('keydown', e => {\n const popoverTrigger = e.target.closest('[data-bs-toggle=\"popover\"]');\n if (e.key === 'Escape' && popoverTrigger) {\n Bootstrap.Popover.getOrCreateInstance(popoverTrigger).hide();\n }\n if (e.key === 'Enter' && popoverTrigger) {\n Bootstrap.Popover.getOrCreateInstance(popoverTrigger).show();\n }\n });\n document.addEventListener('click', e => {\n const popoverTrigger = e.target.closest('[data-bs-toggle=\"popover\"]');\n if (!popoverTrigger) {\n return;\n }\n const popover = Bootstrap.Popover.getOrCreateInstance(popoverTrigger);\n if (!popover._isShown()) {\n popover.show();\n }\n });\n};\n\n/**\n * Enable tooltips\n *\n * @param {Element} rootElement\n */\nconst enableTooltips = (rootElement = document) => {\n const tooltipTriggerList = rootElement.querySelectorAll('[data-bs-toggle=\"tooltip\"]');\n const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new Bootstrap.Tooltip(tooltipTriggerEl));\n\n document.addEventListener('keydown', e => {\n if (e.key === 'Escape') {\n tooltipList.forEach(tooltip => {\n tooltip.hide();\n });\n }\n });\n};\n\n/**\n * Enable tooltips for dynamic content updates\n */\nconst enableTooltipsOnContentUpdated = () => {\n document.addEventListener(eventTypes.filterContentUpdated, e => {\n e.detail.nodes.forEach(node => {\n if (node instanceof HTMLElement) {\n enableTooltips(node);\n }\n });\n });\n};\n\n/**\n * Realocate Bootstrap events to the body element.\n *\n * Bootstrap 5 has a unique event handling mechanism that attaches all event handlers at the document level\n * during the capture phase, rather than the usual bubbling phase. As a result, original Bootstrap events\n * cannot be stopped or prevented, since the document is the first node executed in the capture phase.\n * For certain advanced UI elements, such as form autocomplete, it is important to capture key-down events before\n * Bootstrap's handlers to prevent unintended closures of elements. Therefore, we need to change the Bootstrap handler\n * so that it operates one level lower, specifically at the body level.\n */\nconst realocateBootstrapEvents = () => {\n EventHandler.off(document, 'keydown.bs.dropdown.data-api', '.dropdown-menu', Bootstrap.Dropdown.dataApiKeydownHandler);\n EventHandler.on(document.body, 'keydown.bs.dropdown.data-api', '.dropdown-menu', Bootstrap.Dropdown.dataApiKeydownHandler);\n};\n\nconst pendingPromise = new Pending('theme_boost/loader:init');\n\n// Add pending promise event listeners to relevant Bootstrap custom events.\nsetupBootstrapPendingChecks();\n\n// Setup Aria helpers for Bootstrap features.\nAria.init();\n\n// Remember the last visited tabs.\nrememberTabs();\n\n// Enable all popovers.\nenablePopovers();\n\n// Enable all tooltips.\nenableTooltips();\nenableTooltipsOnContentUpdated();\n\n// Realocate Bootstrap events to the body element.\nrealocateBootstrapEvents();\n\npendingPromise.resolve();\n\nexport {\n Bootstrap,\n};\n"],"names":["enableTooltips","rootElement","document","tooltipTriggerList","querySelectorAll","tooltipList","map","tooltipTriggerEl","Bootstrap","Tooltip","addEventListener","e","key","forEach","tooltip","hide","pendingPromise","Pending","Aria","init","tabTriggerEl","hash","target","getAttribute","history","replaceState","location","window","tab","querySelector","click","rememberTabs","popoverTriggerList","popoverConfig","container","trigger","allowList","Object","assign","DefaultAllowlist","table","thead","tbody","tr","th","td","popoverTriggerEl","Popover","popoverTrigger","closest","getOrCreateInstance","show","popover","_isShown","enablePopovers","eventTypes","filterContentUpdated","detail","nodes","node","HTMLElement","off","Dropdown","dataApiKeydownHandler","on","body","resolve"],"mappings":";;;;;;;;oVAoGMA,eAAiB,eAACC,mEAAcC,eAC5BC,mBAAqBF,YAAYG,iBAAiB,8BAClDC,YAAc,IAAIF,oBAAoBG,KAAIC,kBAAoB,IAAIC,UAAUC,QAAQF,oBAE1FL,SAASQ,iBAAiB,WAAWC,IACnB,WAAVA,EAAEC,KACFP,YAAYQ,SAAQC,UAChBA,QAAQC,cAkClBC,eAAiB,IAAIC,iBAAQ,mDAMnCC,KAAKC,OAhHgB,UACMjB,SAASE,iBAAiB,4BAC7BE,KAAIc,cAAgBA,aAAaV,iBAAiB,gBAAiBC,QAC/EU,KAAOV,EAAEW,OAAOC,aAAa,QAC7BC,QAAQC,aACRD,QAAQC,aAAa,KAAM,KAAMJ,MAEjCK,SAASL,KAAOA,gBAGlBA,KAAOM,OAAOD,SAASL,QACzBA,KAAM,OACAO,IAAM1B,SAAS2B,cAAc,2BAA6BR,KAAO,MACnEO,KACAA,IAAIE,UAqGhBC,GA5FuB,YACbC,mBAAqB9B,SAASE,iBAAiB,8BAC/C6B,cAAgB,CAClBC,UAAW,OACXC,QAAS,QACTC,UAAWC,OAAOC,OAAOC,4BAAkB,CAACC,MAAO,GAAIC,MAAO,GAAIC,MAAO,GAAIC,GAAI,GAAIC,GAAI,GAAIC,GAAI,UAEjGb,oBAAoB1B,KAAIwC,kBAAoB,IAAItC,UAAUuC,QAAQD,iBAAkBb,iBAGxF/B,SAASQ,iBAAiB,2BAA4BC,QACpCA,EAAEW,OACiBlB,iBAAiB,+BAC1BE,KAAIwC,kBAAoB,IAAItC,UAAUuC,QAAQD,iBAAkBb,oBAG5F/B,SAASQ,iBAAiB,WAAWC,UAC3BqC,eAAiBrC,EAAEW,OAAO2B,QAAQ,8BAC1B,WAAVtC,EAAEC,KAAoBoC,gBACtBxC,UAAUuC,QAAQG,oBAAoBF,gBAAgBjC,OAE5C,UAAVJ,EAAEC,KAAmBoC,gBACrBxC,UAAUuC,QAAQG,oBAAoBF,gBAAgBG,UAG9DjD,SAASQ,iBAAiB,SAASC,UACzBqC,eAAiBrC,EAAEW,OAAO2B,QAAQ,kCACnCD,4BAGCI,QAAU5C,UAAUuC,QAAQG,oBAAoBF,gBACjDI,QAAQC,YACTD,QAAQD,WA+DpBG,GAGAtD,iBAvCIE,SAASQ,iBAAiB6C,mBAAWC,sBAAsB7C,IACvDA,EAAE8C,OAAOC,MAAM7C,SAAQ8C,OACfA,gBAAgBC,aAChB5D,eAAe2D,kCAiBdE,IAAI3D,SAAU,+BAAgC,iBAAkBM,UAAUsD,SAASC,6CACnFC,GAAG9D,SAAS+D,KAAM,+BAAgC,iBAAkBzD,UAAUsD,SAASC,uBAwBxG/C,eAAekD"} \ No newline at end of file diff --git a/public/theme/boost/amd/src/loader.js b/public/theme/boost/amd/src/loader.js index fe72f1a7d0c..1b5c49bf557 100644 --- a/public/theme/boost/amd/src/loader.js +++ b/public/theme/boost/amd/src/loader.js @@ -25,6 +25,7 @@ import * as Aria from './aria'; import * as Bootstrap from './index'; import Pending from 'core/pending'; +import {eventTypes} from 'core_filters/events'; import {DefaultAllowlist} from './bootstrap/util/sanitizer'; import setupBootstrapPendingChecks from './pending'; import EventHandler from './bootstrap/dom/event-handler'; @@ -95,9 +96,10 @@ const enablePopovers = () => { /** * Enable tooltips * + * @param {Element} rootElement */ -const enableTooltips = () => { - const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]'); +const enableTooltips = (rootElement = document) => { + const tooltipTriggerList = rootElement.querySelectorAll('[data-bs-toggle="tooltip"]'); const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new Bootstrap.Tooltip(tooltipTriggerEl)); document.addEventListener('keydown', e => { @@ -109,6 +111,19 @@ const enableTooltips = () => { }); }; +/** + * Enable tooltips for dynamic content updates + */ +const enableTooltipsOnContentUpdated = () => { + document.addEventListener(eventTypes.filterContentUpdated, e => { + e.detail.nodes.forEach(node => { + if (node instanceof HTMLElement) { + enableTooltips(node); + } + }); + }); +}; + /** * Realocate Bootstrap events to the body element. * @@ -140,6 +155,7 @@ enablePopovers(); // Enable all tooltips. enableTooltips(); +enableTooltipsOnContentUpdated(); // Realocate Bootstrap events to the body element. realocateBootstrapEvents();