From 01687d9125ec8fde45a8d6fe58f3a144747d5a01 Mon Sep 17 00:00:00 2001 From: Kevin Percy Date: Tue, 5 Sep 2023 11:13:53 +0800 Subject: [PATCH 1/4] MDL-78813 gradereport_grader: Fixed help icon covering course average --- grade/report/grader/styles.css | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/grade/report/grader/styles.css b/grade/report/grader/styles.css index ff27213edb4..c1a9732adc5 100644 --- a/grade/report/grader/styles.css +++ b/grade/report/grader/styles.css @@ -269,3 +269,14 @@ width: 300px; max-height: 300px; } + +@media only screen and (min-width: 768px) { + .path-grade-report-grader .gradeparent table { + padding-right: 6em; + border-top-width: 0; + } + + .path-grade-report-grader .gradeparent table tr:first-of-type th { + border-top-width: 1px; + } +} From 4fa621a62f504ef01d31d8fd8b932332cff6e1e9 Mon Sep 17 00:00:00 2001 From: Mihail Geshoski Date: Mon, 25 Sep 2023 22:43:25 +0200 Subject: [PATCH 2/4] MDL-78813 core: Event to notify when the sticky footer has been toggled New event in core/sticky-footer that is fired when the sticky footer has been enabled or disabled. Leveraging this event, modules can create event listeners and execute actions once the sticky footer has been toggled. --- lib/amd/build/sticky-footer.min.js | 4 ++-- lib/amd/build/sticky-footer.min.js.map | 2 +- lib/amd/src/sticky-footer.js | 18 +++++++++++++++++- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/lib/amd/build/sticky-footer.min.js b/lib/amd/build/sticky-footer.min.js index 287bab9040d..f8ef177f41c 100644 --- a/lib/amd/build/sticky-footer.min.js +++ b/lib/amd/build/sticky-footer.min.js @@ -1,4 +1,4 @@ -define("core/sticky-footer",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.registerManager=_exports.init=_exports.enableStickyFooter=_exports.disableStickyFooter=void 0; +define("core/sticky-footer",["exports","core/event_dispatcher"],(function(_exports,_event_dispatcher){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.registerManager=_exports.init=_exports.eventTypes=_exports.enableStickyFooter=_exports.disableStickyFooter=_exports.SELECTORS=void 0; /** * Sticky footer wrapper module. * @@ -9,6 +9,6 @@ define("core/sticky-footer",["exports"],(function(_exports){Object.definePropert * @copyright 2023 Ferran Recio * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -let manager={},enabled=!1,initialized=!1;const SELECTORS_STICKYFOOTER=".stickyfooter",CLASSES_INVISIBLE="v-hidden",enableStickyFooter=()=>{var _document$querySelect;(enabled=!0,void 0!==manager.enableStickyFooter)?manager.enableStickyFooter():null===(_document$querySelect=document.querySelector(SELECTORS_STICKYFOOTER))||void 0===_document$querySelect||_document$querySelect.classList.remove(CLASSES_INVISIBLE)};_exports.enableStickyFooter=enableStickyFooter;const disableStickyFooter=()=>{var _document$querySelect2;(enabled=!1,void 0!==manager.disableStickyFooter)?manager.disableStickyFooter():null===(_document$querySelect2=document.querySelector(SELECTORS_STICKYFOOTER))||void 0===_document$querySelect2||_document$querySelect2.classList.add(CLASSES_INVISIBLE)};_exports.disableStickyFooter=disableStickyFooter;_exports.registerManager=themeManager=>{manager=themeManager,enabled&&enableStickyFooter()};_exports.init=()=>{var _document$querySelect3;if(initialized)return;initialized=!0;(null===(_document$querySelect3=document.querySelector(SELECTORS_STICKYFOOTER))||void 0===_document$querySelect3?void 0:_document$querySelect3.dataset.disable)?disableStickyFooter():enableStickyFooter()}})); +let manager={},enabled=!1,initialized=!1;const SELECTORS={STICKYFOOTER:".stickyfooter"};_exports.SELECTORS=SELECTORS;const CLASSES_INVISIBLE="v-hidden",eventTypes={stickyFooterStateChanged:"core/stickyfooter_state_changed"};_exports.eventTypes=eventTypes;const enableStickyFooter=()=>{var _document$querySelect;(enabled=!0,void 0!==manager.enableStickyFooter)?(manager.enableStickyFooter(),notifyStickyFooterStateChange(!0)):null===(_document$querySelect=document.querySelector(SELECTORS.STICKYFOOTER))||void 0===_document$querySelect||_document$querySelect.classList.remove(CLASSES_INVISIBLE)};_exports.enableStickyFooter=enableStickyFooter;const disableStickyFooter=()=>{var _document$querySelect2;(enabled=!1,void 0!==manager.disableStickyFooter)?(manager.disableStickyFooter(),notifyStickyFooterStateChange(!1)):null===(_document$querySelect2=document.querySelector(SELECTORS.STICKYFOOTER))||void 0===_document$querySelect2||_document$querySelect2.classList.add(CLASSES_INVISIBLE)};_exports.disableStickyFooter=disableStickyFooter;_exports.registerManager=themeManager=>{manager=themeManager,enabled&&enableStickyFooter()};_exports.init=()=>{var _document$querySelect3;if(initialized)return;initialized=!0;(null===(_document$querySelect3=document.querySelector(SELECTORS.STICKYFOOTER))||void 0===_document$querySelect3?void 0:_document$querySelect3.dataset.disable)?disableStickyFooter():enableStickyFooter()};const notifyStickyFooterStateChange=enabled=>{(0,_event_dispatcher.dispatchEvent)(eventTypes.stickyFooterStateChanged,{enabled:enabled},document,{cancelable:!1})}})); //# sourceMappingURL=sticky-footer.min.js.map \ No newline at end of file diff --git a/lib/amd/build/sticky-footer.min.js.map b/lib/amd/build/sticky-footer.min.js.map index 8e02f6c060a..195cd6ee051 100644 --- a/lib/amd/build/sticky-footer.min.js.map +++ b/lib/amd/build/sticky-footer.min.js.map @@ -1 +1 @@ -{"version":3,"file":"sticky-footer.min.js","sources":["../src/sticky-footer.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 * Sticky footer wrapper module.\n *\n * Themes are responsible for implementing the sticky footer. However,\n * modules can interact with the sticky footer using this module.\n *\n * @module core/sticky-footer\n * @copyright 2023 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\nlet manager = {};\n\nlet enabled = false;\n\nlet initialized = false;\n\nconst SELECTORS = {\n STICKYFOOTER: '.stickyfooter',\n};\n\nconst CLASSES = {\n INVISIBLE: 'v-hidden',\n};\n\n/**\n * Enable sticky footer in the page.\n */\nexport const enableStickyFooter = () => {\n enabled = true;\n if (manager.enableStickyFooter === undefined) {\n document.querySelector(SELECTORS.STICKYFOOTER)?.classList.remove(CLASSES.INVISIBLE);\n return;\n }\n manager.enableStickyFooter();\n};\n\n/**\n * Disable sticky footer in the page.\n */\nexport const disableStickyFooter = () => {\n enabled = false;\n if (manager.disableStickyFooter === undefined) {\n document.querySelector(SELECTORS.STICKYFOOTER)?.classList.add(CLASSES.INVISIBLE);\n return;\n }\n manager.disableStickyFooter();\n};\n\n/**\n * Register the theme sticky footer methods.\n *\n * @param {Object} themeManager the manager object with all the needed methods.\n * @param {Function} themeManager.enableStickyFooter enable sticky footer method\n * @param {Function} themeManager.disableStickyFooter disable sticky footer method\n */\nexport const registerManager = (themeManager) => {\n manager = themeManager;\n if (enabled) {\n enableStickyFooter();\n }\n};\n\n/**\n * Initialize the module if the theme does not implement its own init.\n */\nexport const init = () => {\n if (initialized) {\n return;\n }\n initialized = true;\n\n const isDisabled = document.querySelector(SELECTORS.STICKYFOOTER)?.dataset.disable;\n if (isDisabled) {\n disableStickyFooter();\n } else {\n enableStickyFooter();\n }\n};\n"],"names":["manager","enabled","initialized","SELECTORS","CLASSES","enableStickyFooter","undefined","document","querySelector","classList","remove","disableStickyFooter","add","themeManager","_document$querySelect3","dataset","disable"],"mappings":";;;;;;;;;;;IA2BIA,QAAU,GAEVC,SAAU,EAEVC,aAAc,QAEZC,uBACY,gBAGZC,kBACS,WAMFC,mBAAqB,gCAC9BJ,SAAU,OACyBK,IAA/BN,QAAQK,oBAIZL,QAAQK,mDAHJE,SAASC,cAAcL,gFAAyBM,UAAUC,OAAON,yEAS5DO,oBAAsB,iCAC/BV,SAAU,OAC0BK,IAAhCN,QAAQW,qBAIZX,QAAQW,qDAHJJ,SAASC,cAAcL,kFAAyBM,UAAUG,IAAIR,8FAatCS,eAC5Bb,QAAUa,aACNZ,SACAI,oCAOY,mCACZH,mBAGJA,aAAc,kCAEKK,SAASC,cAAcL,iEAAvBW,uBAAgDC,QAAQC,SAEvEL,sBAEAN"} \ No newline at end of file +{"version":3,"file":"sticky-footer.min.js","sources":["../src/sticky-footer.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 * Sticky footer wrapper module.\n *\n * Themes are responsible for implementing the sticky footer. However,\n * modules can interact with the sticky footer using this module.\n *\n * @module core/sticky-footer\n * @copyright 2023 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {dispatchEvent} from 'core/event_dispatcher';\n\nlet manager = {};\n\nlet enabled = false;\n\nlet initialized = false;\n\nexport const SELECTORS = {\n STICKYFOOTER: '.stickyfooter',\n};\n\nconst CLASSES = {\n INVISIBLE: 'v-hidden',\n};\n\nexport const eventTypes = {\n stickyFooterStateChanged: 'core/stickyfooter_state_changed',\n};\n\n/**\n * Enable sticky footer in the page.\n */\nexport const enableStickyFooter = () => {\n enabled = true;\n if (manager.enableStickyFooter === undefined) {\n document.querySelector(SELECTORS.STICKYFOOTER)?.classList.remove(CLASSES.INVISIBLE);\n return;\n }\n manager.enableStickyFooter();\n notifyStickyFooterStateChange(true);\n};\n\n/**\n * Disable sticky footer in the page.\n */\nexport const disableStickyFooter = () => {\n enabled = false;\n if (manager.disableStickyFooter === undefined) {\n document.querySelector(SELECTORS.STICKYFOOTER)?.classList.add(CLASSES.INVISIBLE);\n return;\n }\n manager.disableStickyFooter();\n notifyStickyFooterStateChange(false);\n};\n\n/**\n * Register the theme sticky footer methods.\n *\n * @param {Object} themeManager the manager object with all the needed methods.\n * @param {Function} themeManager.enableStickyFooter enable sticky footer method\n * @param {Function} themeManager.disableStickyFooter disable sticky footer method\n */\nexport const registerManager = (themeManager) => {\n manager = themeManager;\n if (enabled) {\n enableStickyFooter();\n }\n};\n\n/**\n * Initialize the module if the theme does not implement its own init.\n */\nexport const init = () => {\n if (initialized) {\n return;\n }\n initialized = true;\n\n const isDisabled = document.querySelector(SELECTORS.STICKYFOOTER)?.dataset.disable;\n if (isDisabled) {\n disableStickyFooter();\n } else {\n enableStickyFooter();\n }\n};\n\n/**\n * Dispatch an event to notify that the state (enabled/disabled) of the sticky footer has changed.\n *\n * @param {boolean} enabled Whether the sticky footer has been enabled or disabled.\n */\nconst notifyStickyFooterStateChange = (enabled) => {\n dispatchEvent(eventTypes.stickyFooterStateChanged, {enabled: enabled}, document, {cancelable: false});\n};\n"],"names":["manager","enabled","initialized","SELECTORS","STICKYFOOTER","CLASSES","eventTypes","stickyFooterStateChanged","enableStickyFooter","undefined","notifyStickyFooterStateChange","document","querySelector","classList","remove","disableStickyFooter","add","themeManager","_document$querySelect3","dataset","disable","cancelable"],"mappings":";;;;;;;;;;;IA4BIA,QAAU,GAEVC,SAAU,EAEVC,aAAc,QAELC,UAAY,CACrBC,aAAc,oDAGZC,kBACS,WAGFC,WAAa,CACtBC,yBAA0B,wEAMjBC,mBAAqB,gCAC9BP,SAAU,OACyBQ,IAA/BT,QAAQQ,qBAIZR,QAAQQ,qBACRE,+BAA8B,kCAJ1BC,SAASC,cAAcT,UAAUC,sEAAeS,UAAUC,OAAOT,yEAU5DU,oBAAsB,iCAC/Bd,SAAU,OAC0BQ,IAAhCT,QAAQe,sBAIZf,QAAQe,sBACRL,+BAA8B,mCAJ1BC,SAASC,cAAcT,UAAUC,wEAAeS,UAAUG,IAAIX,8FActCY,eAC5BjB,QAAUiB,aACNhB,SACAO,oCAOY,mCACZN,mBAGJA,aAAc,kCAEKS,SAASC,cAAcT,UAAUC,uDAAjCc,uBAAgDC,QAAQC,SAEvEL,sBAEAP,4BASFE,8BAAiCT,8CACrBK,WAAWC,yBAA0B,CAACN,QAASA,SAAUU,SAAU,CAACU,YAAY"} \ No newline at end of file diff --git a/lib/amd/src/sticky-footer.js b/lib/amd/src/sticky-footer.js index 00d10388072..3f20f60f813 100644 --- a/lib/amd/src/sticky-footer.js +++ b/lib/amd/src/sticky-footer.js @@ -24,6 +24,7 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +import {dispatchEvent} from 'core/event_dispatcher'; let manager = {}; @@ -31,7 +32,7 @@ let enabled = false; let initialized = false; -const SELECTORS = { +export const SELECTORS = { STICKYFOOTER: '.stickyfooter', }; @@ -39,6 +40,10 @@ const CLASSES = { INVISIBLE: 'v-hidden', }; +export const eventTypes = { + stickyFooterStateChanged: 'core/stickyfooter_state_changed', +}; + /** * Enable sticky footer in the page. */ @@ -49,6 +54,7 @@ export const enableStickyFooter = () => { return; } manager.enableStickyFooter(); + notifyStickyFooterStateChange(true); }; /** @@ -61,6 +67,7 @@ export const disableStickyFooter = () => { return; } manager.disableStickyFooter(); + notifyStickyFooterStateChange(false); }; /** @@ -93,3 +100,12 @@ export const init = () => { enableStickyFooter(); } }; + +/** + * Dispatch an event to notify that the state (enabled/disabled) of the sticky footer has changed. + * + * @param {boolean} enabled Whether the sticky footer has been enabled or disabled. + */ +const notifyStickyFooterStateChange = (enabled) => { + dispatchEvent(eventTypes.stickyFooterStateChanged, {enabled: enabled}, document, {cancelable: false}); +}; From 8b9efc31357732123b4cec1ddd78e885cdddda5d Mon Sep 17 00:00:00 2001 From: Mihail Geshoski Date: Mon, 25 Sep 2023 22:53:14 +0200 Subject: [PATCH 3/4] MDL-78813 gradereport_grader: Make the average row sticky again After MDL-75762 the overall average row in the grader report was no longer properly pinned and hidden behind the sticky footer. This change ensures that the overall average row is pinned again at the top of the sticky footer when vertically scrolling. Also, leveragning on the new sticky footer 'toggled' event it is making sure that the row is properly pinned on narrower screens where the sticky footer appears and disappears dynamically when scrolling. --- .../grader/amd/build/stickycolspan.min.js | 4 +- .../grader/amd/build/stickycolspan.min.js.map | 2 +- grade/report/grader/amd/src/stickycolspan.js | 38 +++++++++++++++---- theme/boost/scss/moodle/grade.scss | 10 +++++ theme/boost/style/moodle.css | 3 ++ theme/classic/style/moodle.css | 3 ++ 6 files changed, 49 insertions(+), 11 deletions(-) diff --git a/grade/report/grader/amd/build/stickycolspan.min.js b/grade/report/grader/amd/build/stickycolspan.min.js index 380ff0c5fb0..c94cba67556 100644 --- a/grade/report/grader/amd/build/stickycolspan.min.js +++ b/grade/report/grader/amd/build/stickycolspan.min.js @@ -1,10 +1,10 @@ -define("gradereport_grader/stickycolspan",["exports","jquery"],(function(_exports,_jquery){var obj; +define("gradereport_grader/stickycolspan",["exports","jquery","core/sticky-footer"],(function(_exports,_jquery,_stickyFooter){var obj; /** * Javascript module for fixing the position of sticky headers with multiple colspans * * @module gradereport_grader/stickycolspan * @copyright 2022 Bas Brands * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_jquery=(obj=_jquery)&&obj.__esModule?obj:{default:obj};const SELECTORS_GRADEPARENT=".gradeparent",SELECTORS_STUDENTHEADER="#studentheader",SELECTORS_TABLEHEADER="th.header",SELECTORS_BEHAT="body.behat-site",SELECTORS_USERDROPDOWN=".userrow th .dropdown",SELECTORS_AVERAGEROWHEADER=".lastrow th";_exports.init=()=>{(0,_jquery.default)(SELECTORS_USERDROPDOWN).on("show.bs.dropdown hide.bs.dropdown",(e=>{e.target.closest(SELECTORS_TABLEHEADER).classList.toggle("actions-menu-active")}));if(new IntersectionObserver((_ref=>{let[e]=_ref;return e.target.closest("tr").classList.toggle("pinned",e.intersectionRatio<1)}),{threshold:[1]}).observe(document.querySelector(SELECTORS_AVERAGEROWHEADER)),!document.querySelector(SELECTORS_BEHAT)){const grader=document.querySelector(SELECTORS_GRADEPARENT),tableHeaders=grader.querySelectorAll(SELECTORS_TABLEHEADER),studentHeader=grader.querySelector(SELECTORS_STUDENTHEADER),leftOffset=getComputedStyle(studentHeader).getPropertyValue("left"),rightOffset=getComputedStyle(studentHeader).getPropertyValue("right");tableHeaders.forEach((tableHeader=>{if(tableHeader.colSpan>1){const addOffset=tableHeader.offsetWidth-studentHeader.offsetWidth;window.right_to_left()?tableHeader.style.right="calc("+rightOffset+" - "+addOffset+"px )":tableHeader.style.left="calc("+leftOffset+" - "+addOffset+"px )"}}))}}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_jquery=(obj=_jquery)&&obj.__esModule?obj:{default:obj};const SELECTORS_GRADEPARENT=".gradeparent",SELECTORS_STUDENTHEADER="#studentheader",SELECTORS_TABLEHEADER="th.header",SELECTORS_BEHAT="body.behat-site",SELECTORS_USERDROPDOWN=".userrow th .dropdown",SELECTORS_AVERAGEROWHEADER=".lastrow th";_exports.init=()=>{if((0,_jquery.default)(SELECTORS_USERDROPDOWN).on("show.bs.dropdown hide.bs.dropdown",(e=>{e.target.closest(SELECTORS_TABLEHEADER).classList.toggle("actions-menu-active")})),defineAverageRowIntersectionObserver(!0),document.addEventListener(_stickyFooter.eventTypes.stickyFooterStateChanged,(e=>{defineAverageRowIntersectionObserver(e.detail.enabled)})),!document.querySelector(SELECTORS_BEHAT)){const grader=document.querySelector(SELECTORS_GRADEPARENT),tableHeaders=grader.querySelectorAll(SELECTORS_TABLEHEADER),studentHeader=grader.querySelector(SELECTORS_STUDENTHEADER),leftOffset=getComputedStyle(studentHeader).getPropertyValue("left"),rightOffset=getComputedStyle(studentHeader).getPropertyValue("right");tableHeaders.forEach((tableHeader=>{if(tableHeader.colSpan>1){const addOffset=tableHeader.offsetWidth-studentHeader.offsetWidth;window.right_to_left()?tableHeader.style.right="calc("+rightOffset+" - "+addOffset+"px )":tableHeader.style.left="calc("+leftOffset+" - "+addOffset+"px )"}}))}};const defineAverageRowIntersectionObserver=stickyFooterEnabled=>{const stickyFooterHeight=stickyFooterEnabled?document.querySelector(_stickyFooter.SELECTORS.STICKYFOOTER).offsetHeight:null;new IntersectionObserver((_ref=>{let[e]=_ref;return e.target.closest("tr").classList.toggle("pinned",e.intersectionRatio<1)}),{rootMargin:stickyFooterHeight?"0px 0px -".concat(stickyFooterHeight,"px 0px"):"0px",threshold:[1]}).observe(document.querySelector(SELECTORS_AVERAGEROWHEADER))}})); //# sourceMappingURL=stickycolspan.min.js.map \ No newline at end of file diff --git a/grade/report/grader/amd/build/stickycolspan.min.js.map b/grade/report/grader/amd/build/stickycolspan.min.js.map index 48523d6faa7..6ba16729a30 100644 --- a/grade/report/grader/amd/build/stickycolspan.min.js.map +++ b/grade/report/grader/amd/build/stickycolspan.min.js.map @@ -1 +1 @@ -{"version":3,"file":"stickycolspan.min.js","sources":["../src/stickycolspan.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 * Javascript module for fixing the position of sticky headers with multiple colspans\n *\n * @module gradereport_grader/stickycolspan\n * @copyright 2022 Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\n\nconst SELECTORS = {\n GRADEPARENT: '.gradeparent',\n STUDENTHEADER: '#studentheader',\n TABLEHEADER: 'th.header',\n BEHAT: 'body.behat-site',\n USERDROPDOWN: '.userrow th .dropdown',\n AVERAGEROWHEADER: '.lastrow th',\n};\n\n/**\n * Initialize module\n */\nexport const init = () => {\n // The sticky positioning attributed to the user column cells affects the stacking context and makes the dropdowns\n // within these cells to be cut off. To solve this problem, whenever one of these action menus (dropdowns) is opened\n // we need to manually bump up the z-index value of the parent container element and revert once closed.\n $(SELECTORS.USERDROPDOWN).on('show.bs.dropdown hide.bs.dropdown', (e) => {\n // The closest heading element has sticky positioning which affects the stacking context in this case.\n e.target.closest(SELECTORS.TABLEHEADER).classList.toggle('actions-menu-active');\n });\n // Register an observer that will bump up the z-index value of the average overall row when it's pinned to prevent\n // the row being cut-off by the user column cells or other components within the report table that have higher\n // z-index values.\n const observer = new IntersectionObserver(\n ([e]) => e.target.closest('tr').classList.toggle('pinned', e.intersectionRatio < 1),\n {threshold: [1]}\n );\n observer.observe(document.querySelector(SELECTORS.AVERAGEROWHEADER));\n\n if (!document.querySelector(SELECTORS.BEHAT)) {\n const grader = document.querySelector(SELECTORS.GRADEPARENT);\n const tableHeaders = grader.querySelectorAll(SELECTORS.TABLEHEADER);\n const studentHeader = grader.querySelector(SELECTORS.STUDENTHEADER);\n const leftOffset = getComputedStyle(studentHeader).getPropertyValue('left');\n const rightOffset = getComputedStyle(studentHeader).getPropertyValue('right');\n\n tableHeaders.forEach((tableHeader) => {\n if (tableHeader.colSpan > 1) {\n const addOffset = (tableHeader.offsetWidth - studentHeader.offsetWidth);\n if (window.right_to_left()) {\n tableHeader.style.right = 'calc(' + rightOffset + ' - ' + addOffset + 'px )';\n } else {\n tableHeader.style.left = 'calc(' + leftOffset + ' - ' + addOffset + 'px )';\n }\n }\n });\n }\n};\n"],"names":["SELECTORS","on","e","target","closest","classList","toggle","IntersectionObserver","_ref","intersectionRatio","threshold","observe","document","querySelector","grader","tableHeaders","querySelectorAll","studentHeader","leftOffset","getComputedStyle","getPropertyValue","rightOffset","forEach","tableHeader","colSpan","addOffset","offsetWidth","window","right_to_left","style","right","left"],"mappings":";;;;;;;gJAyBMA,sBACW,eADXA,wBAEa,iBAFbA,sBAGW,YAHXA,gBAIK,kBAJLA,uBAKY,wBALZA,2BAMgB,4BAMF,yBAIdA,wBAAwBC,GAAG,qCAAsCC,IAE/DA,EAAEC,OAAOC,QAAQJ,uBAAuBK,UAAUC,OAAO,6BAK5C,IAAIC,sBACjBC,WAAEN,eAAOA,EAAEC,OAAOC,QAAQ,MAAMC,UAAUC,OAAO,SAAUJ,EAAEO,kBAAoB,KACjF,CAACC,UAAW,CAAC,KAERC,QAAQC,SAASC,cAAcb,8BAEnCY,SAASC,cAAcb,iBAAkB,OACpCc,OAASF,SAASC,cAAcb,uBAChCe,aAAeD,OAAOE,iBAAiBhB,uBACvCiB,cAAgBH,OAAOD,cAAcb,yBACrCkB,WAAaC,iBAAiBF,eAAeG,iBAAiB,QAC9DC,YAAcF,iBAAiBF,eAAeG,iBAAiB,SAErEL,aAAaO,SAASC,iBACdA,YAAYC,QAAU,EAAG,OACnBC,UAAaF,YAAYG,YAAcT,cAAcS,YACvDC,OAAOC,gBACPL,YAAYM,MAAMC,MAAQ,QAAUT,YAAc,MAAQI,UAAY,OAEtEF,YAAYM,MAAME,KAAO,QAAUb,WAAa,MAAQO,UAAY"} \ No newline at end of file +{"version":3,"file":"stickycolspan.min.js","sources":["../src/stickycolspan.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 * Javascript module for fixing the position of sticky headers with multiple colspans\n *\n * @module gradereport_grader/stickycolspan\n * @copyright 2022 Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport {SELECTORS as stickyFooterSelectors, eventTypes as stickyFooterEvents} from 'core/sticky-footer';\n\nconst SELECTORS = {\n GRADEPARENT: '.gradeparent',\n STUDENTHEADER: '#studentheader',\n TABLEHEADER: 'th.header',\n BEHAT: 'body.behat-site',\n USERDROPDOWN: '.userrow th .dropdown',\n AVERAGEROWHEADER: '.lastrow th',\n};\n\n/**\n * Initialize module\n */\nexport const init = () => {\n // The sticky positioning attributed to the user column cells affects the stacking context and makes the dropdowns\n // within these cells to be cut off. To solve this problem, whenever one of these action menus (dropdowns) is opened\n // we need to manually bump up the z-index value of the parent container element and revert once closed.\n $(SELECTORS.USERDROPDOWN).on('show.bs.dropdown hide.bs.dropdown', (e) => {\n // The closest heading element has sticky positioning which affects the stacking context in this case.\n e.target.closest(SELECTORS.TABLEHEADER).classList.toggle('actions-menu-active');\n });\n\n defineAverageRowIntersectionObserver(true);\n // Add an event listener to the sticky footer toggled event to re-define the average row intersection observer\n // accordingly. This is needed as on narrow screens when scrolling vertically the sticky footer is enabled and\n // disabled dynamically.\n document.addEventListener(stickyFooterEvents.stickyFooterStateChanged, (e) => {\n defineAverageRowIntersectionObserver(e.detail.enabled);\n });\n\n if (!document.querySelector(SELECTORS.BEHAT)) {\n const grader = document.querySelector(SELECTORS.GRADEPARENT);\n const tableHeaders = grader.querySelectorAll(SELECTORS.TABLEHEADER);\n const studentHeader = grader.querySelector(SELECTORS.STUDENTHEADER);\n const leftOffset = getComputedStyle(studentHeader).getPropertyValue('left');\n const rightOffset = getComputedStyle(studentHeader).getPropertyValue('right');\n\n tableHeaders.forEach((tableHeader) => {\n if (tableHeader.colSpan > 1) {\n const addOffset = (tableHeader.offsetWidth - studentHeader.offsetWidth);\n if (window.right_to_left()) {\n tableHeader.style.right = 'calc(' + rightOffset + ' - ' + addOffset + 'px )';\n } else {\n tableHeader.style.left = 'calc(' + leftOffset + ' - ' + addOffset + 'px )';\n }\n }\n });\n }\n};\n\n/**\n * Define the intersection observer that will make sure that the average overall row is properly pinned.\n *\n * @param {boolean} stickyFooterEnabled Whether the page shows a sticky footer or not.\n */\nconst defineAverageRowIntersectionObserver = (stickyFooterEnabled) => {\n const stickyFooterHeight = stickyFooterEnabled ? document.querySelector(stickyFooterSelectors.STICKYFOOTER).offsetHeight : null;\n // Register an observer that will bump up the z-index value of the average overall row when it's pinned to prevent\n // the row being cut-off by the user column cells or other components within the report table that have higher\n // z-index values. If the page has a sticky footer, we need to make sure that the bottom root margin of the observer\n // subtracts the height of the sticky footer to prevent the row being cut-off by the footer.\n const intersectionObserver = new IntersectionObserver(\n ([e]) => e.target.closest('tr').classList.toggle('pinned', e.intersectionRatio < 1),\n {\n rootMargin: stickyFooterHeight ? `0px 0px -${stickyFooterHeight}px 0px` : \"0px\",\n threshold: [1]\n }\n );\n intersectionObserver.observe(document.querySelector(SELECTORS.AVERAGEROWHEADER));\n};\n"],"names":["SELECTORS","on","e","target","closest","classList","toggle","defineAverageRowIntersectionObserver","document","addEventListener","stickyFooterEvents","stickyFooterStateChanged","detail","enabled","querySelector","grader","tableHeaders","querySelectorAll","studentHeader","leftOffset","getComputedStyle","getPropertyValue","rightOffset","forEach","tableHeader","colSpan","addOffset","offsetWidth","window","right_to_left","style","right","left","stickyFooterEnabled","stickyFooterHeight","stickyFooterSelectors","STICKYFOOTER","offsetHeight","IntersectionObserver","_ref","intersectionRatio","rootMargin","threshold","observe"],"mappings":";;;;;;;gJA0BMA,sBACW,eADXA,wBAEa,iBAFbA,sBAGW,YAHXA,gBAIK,kBAJLA,uBAKY,wBALZA,2BAMgB,4BAMF,4BAIdA,wBAAwBC,GAAG,qCAAsCC,IAE/DA,EAAEC,OAAOC,QAAQJ,uBAAuBK,UAAUC,OAAO,0BAG7DC,sCAAqC,GAIrCC,SAASC,iBAAiBC,yBAAmBC,0BAA2BT,IACpEK,qCAAqCL,EAAEU,OAAOC,aAG7CL,SAASM,cAAcd,iBAAkB,OACpCe,OAASP,SAASM,cAAcd,uBAChCgB,aAAeD,OAAOE,iBAAiBjB,uBACvCkB,cAAgBH,OAAOD,cAAcd,yBACrCmB,WAAaC,iBAAiBF,eAAeG,iBAAiB,QAC9DC,YAAcF,iBAAiBF,eAAeG,iBAAiB,SAErEL,aAAaO,SAASC,iBACdA,YAAYC,QAAU,EAAG,OACnBC,UAAaF,YAAYG,YAAcT,cAAcS,YACvDC,OAAOC,gBACPL,YAAYM,MAAMC,MAAQ,QAAUT,YAAc,MAAQI,UAAY,OAEtEF,YAAYM,MAAME,KAAO,QAAUb,WAAa,MAAQO,UAAY,mBAYlFnB,qCAAwC0B,4BACpCC,mBAAqBD,oBAAsBzB,SAASM,cAAcqB,wBAAsBC,cAAcC,aAAe,KAK9F,IAAIC,sBAC7BC,WAAErC,eAAOA,EAAEC,OAAOC,QAAQ,MAAMC,UAAUC,OAAO,SAAUJ,EAAEsC,kBAAoB,KACjF,CACIC,WAAYP,sCAAiCA,6BAA6B,MAC1EQ,UAAW,CAAC,KAGCC,QAAQnC,SAASM,cAAcd"} \ No newline at end of file diff --git a/grade/report/grader/amd/src/stickycolspan.js b/grade/report/grader/amd/src/stickycolspan.js index 456b2cc79f4..1ba4d6d509e 100644 --- a/grade/report/grader/amd/src/stickycolspan.js +++ b/grade/report/grader/amd/src/stickycolspan.js @@ -22,6 +22,7 @@ */ import $ from 'jquery'; +import {SELECTORS as stickyFooterSelectors, eventTypes as stickyFooterEvents} from 'core/sticky-footer'; const SELECTORS = { GRADEPARENT: '.gradeparent', @@ -43,14 +44,14 @@ export const init = () => { // The closest heading element has sticky positioning which affects the stacking context in this case. e.target.closest(SELECTORS.TABLEHEADER).classList.toggle('actions-menu-active'); }); - // Register an observer that will bump up the z-index value of the average overall row when it's pinned to prevent - // the row being cut-off by the user column cells or other components within the report table that have higher - // z-index values. - const observer = new IntersectionObserver( - ([e]) => e.target.closest('tr').classList.toggle('pinned', e.intersectionRatio < 1), - {threshold: [1]} - ); - observer.observe(document.querySelector(SELECTORS.AVERAGEROWHEADER)); + + defineAverageRowIntersectionObserver(true); + // Add an event listener to the sticky footer toggled event to re-define the average row intersection observer + // accordingly. This is needed as on narrow screens when scrolling vertically the sticky footer is enabled and + // disabled dynamically. + document.addEventListener(stickyFooterEvents.stickyFooterStateChanged, (e) => { + defineAverageRowIntersectionObserver(e.detail.enabled); + }); if (!document.querySelector(SELECTORS.BEHAT)) { const grader = document.querySelector(SELECTORS.GRADEPARENT); @@ -71,3 +72,24 @@ export const init = () => { }); } }; + +/** + * Define the intersection observer that will make sure that the average overall row is properly pinned. + * + * @param {boolean} stickyFooterEnabled Whether the page shows a sticky footer or not. + */ +const defineAverageRowIntersectionObserver = (stickyFooterEnabled) => { + const stickyFooterHeight = stickyFooterEnabled ? document.querySelector(stickyFooterSelectors.STICKYFOOTER).offsetHeight : null; + // Register an observer that will bump up the z-index value of the average overall row when it's pinned to prevent + // the row being cut-off by the user column cells or other components within the report table that have higher + // z-index values. If the page has a sticky footer, we need to make sure that the bottom root margin of the observer + // subtracts the height of the sticky footer to prevent the row being cut-off by the footer. + const intersectionObserver = new IntersectionObserver( + ([e]) => e.target.closest('tr').classList.toggle('pinned', e.intersectionRatio < 1), + { + rootMargin: stickyFooterHeight ? `0px 0px -${stickyFooterHeight}px 0px` : "0px", + threshold: [1] + } + ); + intersectionObserver.observe(document.querySelector(SELECTORS.AVERAGEROWHEADER)); +}; diff --git a/theme/boost/scss/moodle/grade.scss b/theme/boost/scss/moodle/grade.scss index 85f543be1cf..3c6ba65d273 100644 --- a/theme/boost/scss/moodle/grade.scss +++ b/theme/boost/scss/moodle/grade.scss @@ -656,6 +656,16 @@ .show-drawer-left .gradeparent th.header { left: -1rem; } + + &.hasstickyfooter { + .gradeparent { + tr.lastrow { + // Hack used by the observer to help detecting when the sticky 'Overall average' row is pinned when + // sticky footer is present. + bottom: calc(#{$stickyfooter-height} - 1px); + } + } + } } /** diff --git a/theme/boost/style/moodle.css b/theme/boost/style/moodle.css index 872e95baab3..e4cd356c0b8 100644 --- a/theme/boost/style/moodle.css +++ b/theme/boost/style/moodle.css @@ -35868,6 +35868,9 @@ p.arrow_button { .path-grade-report-grader .show-drawer-left .gradeparent th.header { left: -1rem; } +.path-grade-report-grader.hasstickyfooter .gradeparent tr.lastrow { + bottom: calc(max(96px, 0.9375rem * 3) - 1px); +} /** * User report. diff --git a/theme/classic/style/moodle.css b/theme/classic/style/moodle.css index 9c4e2858add..7a66a3bb933 100644 --- a/theme/classic/style/moodle.css +++ b/theme/classic/style/moodle.css @@ -35868,6 +35868,9 @@ p.arrow_button { .path-grade-report-grader .show-drawer-left .gradeparent th.header { left: -1rem; } +.path-grade-report-grader.hasstickyfooter .gradeparent tr.lastrow { + bottom: calc(max(96px, 0.9375rem * 3) - 1px); +} /** * User report. From 65b75f18ccb7ed7e3ae344ea1488354107d5330b Mon Sep 17 00:00:00 2001 From: Mihail Geshoski Date: Tue, 26 Sep 2023 13:23:00 +0200 Subject: [PATCH 4/4] MDL-78813 gradereport_grader: Revert border-top-width css change Revert the modification of the border-top-width css attribute to the grader table and the table header. This change seems to be unnecessary. --- grade/report/grader/styles.css | 5 ----- 1 file changed, 5 deletions(-) diff --git a/grade/report/grader/styles.css b/grade/report/grader/styles.css index c1a9732adc5..6c5c75fbaa3 100644 --- a/grade/report/grader/styles.css +++ b/grade/report/grader/styles.css @@ -273,10 +273,5 @@ @media only screen and (min-width: 768px) { .path-grade-report-grader .gradeparent table { padding-right: 6em; - border-top-width: 0; - } - - .path-grade-report-grader .gradeparent table tr:first-of-type th { - border-top-width: 1px; } }