diff --git a/lib/amd/build/dynamic_tabs.min.js b/lib/amd/build/dynamic_tabs.min.js index c5aa179aecb..9454521ad32 100644 --- a/lib/amd/build/dynamic_tabs.min.js +++ b/lib/amd/build/dynamic_tabs.min.js @@ -1,2 +1,2 @@ -define ("core/dynamic_tabs",["exports","jquery","core/templates","core/loadingicon","core/notification","core/pending","core/str","core/local/repository/dynamic_tabs","core_form/changechecker"],function(a,b,c,d,e,f,g,h,i){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=void 0;b=j(b);c=j(c);e=j(e);f=j(f);function j(a){return a&&a.__esModule?a:{default:a}}function k(a,b){var c=Object.keys(a);if(Object.getOwnPropertySymbols){var d=Object.getOwnPropertySymbols(a);if(b)d=d.filter(function(b){return Object.getOwnPropertyDescriptor(a,b).enumerable});c.push.apply(c,d)}return c}function l(a){for(var b=1,c;ba.length)b=a.length;for(var c=0,d=Array(b);ca.length)b=a.length;for(var c=0,d=Array(b);c.\n\n/**\n * Dynamic Tabs UI element with AJAX loading of tabs content\n *\n * @module core/dynamic_tabs\n * @copyright 2021 David Matamoros based on code from Marina Glancy\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport Templates from 'core/templates';\nimport {addIconToContainer} from 'core/loadingicon';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport {get_strings as getStrings} from 'core/str';\nimport {getContent} from 'core/local/repository/dynamic_tabs';\nimport {isAnyWatchedFormDirty, resetAllFormDirtyStates} from 'core_form/changechecker';\n\nconst SELECTORS = {\n dynamicTabs: '.dynamictabs',\n activeTab: '.dynamictabs .nav-link.active',\n allActiveTabs: '.dynamictabs .nav-link[data-toggle=\"tab\"]:not(.disabled)',\n tabContent: '.dynamictabs .tab-pane [data-tab-content]',\n tabToggle: 'a[data-toggle=\"tab\"]',\n tabPane: '.dynamictabs .tab-pane',\n};\n\nSELECTORS.forTabName = tabName => `.dynamictabs [data-tab-content=\"${tabName}\"]`;\nSELECTORS.forTabId = tabName => `.dynamictabs [data-toggle=\"tab\"][href=\"#${tabName}\"]`;\n\n/**\n * Initialises the tabs view on the page (only one tabs view per page is supported)\n */\nexport const init = () => {\n const tabToggle = $(SELECTORS.tabToggle);\n\n // Listen to click, warn user if they are navigating away with unsaved form changes.\n tabToggle.on('click', (event) => {\n if (!isAnyWatchedFormDirty()) {\n return;\n }\n\n event.preventDefault();\n event.stopPropagation();\n\n getStrings([\n {key: 'changesmade', component: 'moodle'},\n {key: 'changesmadereallygoaway', component: 'moodle'},\n {key: 'confirm', component: 'moodle'},\n ]).then(([strChangesMade, strChangesMadeReally, strConfirm]) =>\n // Reset form dirty state on confirmation, re-trigger the event.\n Notification.confirm(strChangesMade, strChangesMadeReally, strConfirm, null, () => {\n resetAllFormDirtyStates();\n $(event.target).trigger(event.type);\n })\n ).catch(Notification.exception);\n });\n\n // This code listens to Bootstrap events 'show.bs.tab' and 'shown.bs.tab' which is triggered using JQuery and\n // can not be converted yet to native events.\n tabToggle\n .on('show.bs.tab', function() {\n // Clean content from previous tab.\n const previousTabName = getActiveTabName();\n if (previousTabName) {\n const previousTab = document.querySelector(SELECTORS.forTabName(previousTabName));\n previousTab.textContent = '';\n }\n })\n .on('shown.bs.tab', function() {\n const tab = $($(this).attr('href'));\n if (tab.length !== 1) {\n return;\n }\n loadTab(tab.attr('id'));\n });\n\n if (!openTabFromHash()) {\n const tabs = document.querySelector(SELECTORS.allActiveTabs);\n if (tabs) {\n openTab(tabs.getAttribute('aria-controls'));\n } else {\n // We may hide tabs if there is only one available, just load the contents of the first tab.\n const tabPane = document.querySelector(SELECTORS.tabPane);\n if (tabPane) {\n tabPane.classList.add('active', 'show');\n loadTab(tabPane.getAttribute('id'));\n }\n }\n }\n};\n\n/**\n * Returns id/name of the currently active tab\n *\n * @return {String|null}\n */\nconst getActiveTabName = () => {\n const element = document.querySelector(SELECTORS.activeTab);\n return element?.getAttribute('aria-controls') || null;\n};\n\n/**\n * Returns the id/name of the first tab\n *\n * @return {String|null}\n */\nconst getFirstTabName = () => {\n const element = document.querySelector(SELECTORS.tabContent);\n return element?.dataset.tabContent || null;\n};\n\n/**\n * Loads contents of a tab using an AJAX request\n *\n * @param {String} tabName\n * @param {Object} additionalData additional data to pass to WS\n */\nconst loadTab = (tabName, additionalData = {}) => {\n // If tabName is not specified find the active tab, or if is not defined, the first available tab.\n tabName = tabName ?? getActiveTabName() ?? getFirstTabName();\n\n const tab = document.querySelector(SELECTORS.forTabName(tabName));\n if (!tab) {\n return;\n }\n\n const pendingPromise = new Pending('core/dynamic_tabs:loadTab:' + tabName);\n const tabdata = tab.closest(SELECTORS.dynamicTabs);\n const wsData = {\n 'reportid': tabdata.dataset.reportid,\n 'id': tabdata.dataset.id,\n ...additionalData\n };\n let tabjs = '';\n\n addIconToContainer(tab)\n .then(() => {\n return getContent(tab.dataset.tabClass, JSON.stringify(wsData));\n })\n .then((data) => {\n tabjs = data.javascript;\n return Templates.render(data.template, JSON.parse(data.content));\n })\n .then((html, js) => {\n return Templates.replaceNodeContents(tab, html, js + tabjs);\n })\n .then(() => {\n pendingPromise.resolve();\n return null;\n })\n .catch(Notification.exception);\n};\n\n/**\n * Return the tab given the tab name\n *\n * @param {String} tabName\n * @return {HTMLElement}\n */\nconst getTab = (tabName) => {\n return document.querySelector(SELECTORS.forTabId(tabName));\n};\n\n/**\n * Return the tab pane given the tab name\n *\n * @param {String} tabName\n * @return {HTMLElement}\n */\nconst getTabPane = (tabName) => {\n return document.getElementById(tabName);\n};\n\n/**\n * Open the tab on page load. If this script loads before theme_boost/tab we need to open tab ourselves\n *\n * @param {String} tabName\n * @return {Boolean}\n */\nconst openTab = (tabName) => {\n const tab = getTab(tabName);\n if (!tab) {\n return false;\n }\n\n loadTab(tabName);\n tab.classList.add('active');\n getTabPane(tabName).classList.add('active', 'show');\n return true;\n};\n\n/**\n * If there is a location hash that is the same as the tab name - open this tab.\n *\n * @return {Boolean}\n */\nconst openTabFromHash = () => {\n const hash = document.location.hash;\n if (hash.match(/^#\\w+$/g)) {\n return openTab(hash.replace(/^#/g, ''));\n }\n\n return false;\n};\n"],"file":"dynamic_tabs.min.js"} \ No newline at end of file +{"version":3,"sources":["../src/dynamic_tabs.js"],"names":["SELECTORS","dynamicTabs","activeTab","allActiveTabs","tabContent","tabToggle","tabPane","forTabName","tabName","forTabId","init","on","event","preventDefault","stopPropagation","key","component","then","strChangesMade","strChangesMadeReally","strConfirm","Notification","confirm","target","trigger","type","catch","exception","previousTabName","getActiveTabName","previousTab","document","querySelector","textContent","tab","attr","length","loadTab","openTabFromHash","tabs","openTab","getAttribute","classList","add","element","getFirstTabName","dataset","pendingPromise","Pending","tabjs","tabArgs","tabClass","JSON","stringify","data","javascript","Templates","render","template","parse","content","html","js","replaceNodeContents","resolve","getTab","getTabPane","getElementById","hash","location","match","replace"],"mappings":"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","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 * Dynamic Tabs UI element with AJAX loading of tabs content\n *\n * @module core/dynamic_tabs\n * @copyright 2021 David Matamoros based on code from Marina Glancy\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport Templates from 'core/templates';\nimport {addIconToContainer} from 'core/loadingicon';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport {get_strings as getStrings} from 'core/str';\nimport {getContent} from 'core/local/repository/dynamic_tabs';\nimport {isAnyWatchedFormDirty, resetAllFormDirtyStates} from 'core_form/changechecker';\n\nconst SELECTORS = {\n dynamicTabs: '.dynamictabs',\n activeTab: '.dynamictabs .nav-link.active',\n allActiveTabs: '.dynamictabs .nav-link[data-toggle=\"tab\"]:not(.disabled)',\n tabContent: '.dynamictabs .tab-pane [data-tab-content]',\n tabToggle: 'a[data-toggle=\"tab\"]',\n tabPane: '.dynamictabs .tab-pane',\n};\n\nSELECTORS.forTabName = tabName => `.dynamictabs [data-tab-content=\"${tabName}\"]`;\nSELECTORS.forTabId = tabName => `.dynamictabs [data-toggle=\"tab\"][href=\"#${tabName}\"]`;\n\n/**\n * Initialises the tabs view on the page (only one tabs view per page is supported)\n */\nexport const init = () => {\n const tabToggle = $(SELECTORS.tabToggle);\n\n // Listen to click, warn user if they are navigating away with unsaved form changes.\n tabToggle.on('click', (event) => {\n if (!isAnyWatchedFormDirty()) {\n return;\n }\n\n event.preventDefault();\n event.stopPropagation();\n\n getStrings([\n {key: 'changesmade', component: 'moodle'},\n {key: 'changesmadereallygoaway', component: 'moodle'},\n {key: 'confirm', component: 'moodle'},\n ]).then(([strChangesMade, strChangesMadeReally, strConfirm]) =>\n // Reset form dirty state on confirmation, re-trigger the event.\n Notification.confirm(strChangesMade, strChangesMadeReally, strConfirm, null, () => {\n resetAllFormDirtyStates();\n $(event.target).trigger(event.type);\n })\n ).catch(Notification.exception);\n });\n\n // This code listens to Bootstrap events 'show.bs.tab' and 'shown.bs.tab' which is triggered using JQuery and\n // can not be converted yet to native events.\n tabToggle\n .on('show.bs.tab', function() {\n // Clean content from previous tab.\n const previousTabName = getActiveTabName();\n if (previousTabName) {\n const previousTab = document.querySelector(SELECTORS.forTabName(previousTabName));\n previousTab.textContent = '';\n }\n })\n .on('shown.bs.tab', function() {\n const tab = $($(this).attr('href'));\n if (tab.length !== 1) {\n return;\n }\n loadTab(tab.attr('id'));\n });\n\n if (!openTabFromHash()) {\n const tabs = document.querySelector(SELECTORS.allActiveTabs);\n if (tabs) {\n openTab(tabs.getAttribute('aria-controls'));\n } else {\n // We may hide tabs if there is only one available, just load the contents of the first tab.\n const tabPane = document.querySelector(SELECTORS.tabPane);\n if (tabPane) {\n tabPane.classList.add('active', 'show');\n loadTab(tabPane.getAttribute('id'));\n }\n }\n }\n};\n\n/**\n * Returns id/name of the currently active tab\n *\n * @return {String|null}\n */\nconst getActiveTabName = () => {\n const element = document.querySelector(SELECTORS.activeTab);\n return element?.getAttribute('aria-controls') || null;\n};\n\n/**\n * Returns the id/name of the first tab\n *\n * @return {String|null}\n */\nconst getFirstTabName = () => {\n const element = document.querySelector(SELECTORS.tabContent);\n return element?.dataset.tabContent || null;\n};\n\n/**\n * Loads contents of a tab using an AJAX request\n *\n * @param {String} tabName\n */\nconst loadTab = (tabName) => {\n // If tabName is not specified find the active tab, or if is not defined, the first available tab.\n tabName = tabName ?? getActiveTabName() ?? getFirstTabName();\n const tab = document.querySelector(SELECTORS.forTabName(tabName));\n if (!tab) {\n return;\n }\n\n const pendingPromise = new Pending('core/dynamic_tabs:loadTab:' + tabName);\n let tabjs = '';\n\n addIconToContainer(tab)\n .then(() => {\n let tabArgs = {...tab.dataset};\n delete tabArgs.tabClass;\n delete tabArgs.tabContent;\n return getContent(tab.dataset.tabClass, JSON.stringify(tabArgs));\n })\n .then((data) => {\n tabjs = data.javascript;\n return Templates.render(data.template, JSON.parse(data.content));\n })\n .then((html, js) => {\n return Templates.replaceNodeContents(tab, html, js + tabjs);\n })\n .then(() => {\n pendingPromise.resolve();\n return null;\n })\n .catch(Notification.exception);\n};\n\n/**\n * Return the tab given the tab name\n *\n * @param {String} tabName\n * @return {HTMLElement}\n */\nconst getTab = (tabName) => {\n return document.querySelector(SELECTORS.forTabId(tabName));\n};\n\n/**\n * Return the tab pane given the tab name\n *\n * @param {String} tabName\n * @return {HTMLElement}\n */\nconst getTabPane = (tabName) => {\n return document.getElementById(tabName);\n};\n\n/**\n * Open the tab on page load. If this script loads before theme_boost/tab we need to open tab ourselves\n *\n * @param {String} tabName\n * @return {Boolean}\n */\nconst openTab = (tabName) => {\n const tab = getTab(tabName);\n if (!tab) {\n return false;\n }\n\n loadTab(tabName);\n tab.classList.add('active');\n getTabPane(tabName).classList.add('active', 'show');\n return true;\n};\n\n/**\n * If there is a location hash that is the same as the tab name - open this tab.\n *\n * @return {Boolean}\n */\nconst openTabFromHash = () => {\n const hash = document.location.hash;\n if (hash.match(/^#\\w+$/g)) {\n return openTab(hash.replace(/^#/g, ''));\n }\n\n return false;\n};\n"],"file":"dynamic_tabs.min.js"} \ No newline at end of file diff --git a/lib/amd/src/dynamic_tabs.js b/lib/amd/src/dynamic_tabs.js index 9e213dec9ee..fca4f3e7201 100644 --- a/lib/amd/src/dynamic_tabs.js +++ b/lib/amd/src/dynamic_tabs.js @@ -128,29 +128,24 @@ const getFirstTabName = () => { * Loads contents of a tab using an AJAX request * * @param {String} tabName - * @param {Object} additionalData additional data to pass to WS */ -const loadTab = (tabName, additionalData = {}) => { +const loadTab = (tabName) => { // If tabName is not specified find the active tab, or if is not defined, the first available tab. tabName = tabName ?? getActiveTabName() ?? getFirstTabName(); - const tab = document.querySelector(SELECTORS.forTabName(tabName)); if (!tab) { return; } const pendingPromise = new Pending('core/dynamic_tabs:loadTab:' + tabName); - const tabdata = tab.closest(SELECTORS.dynamicTabs); - const wsData = { - 'reportid': tabdata.dataset.reportid, - 'id': tabdata.dataset.id, - ...additionalData - }; let tabjs = ''; addIconToContainer(tab) .then(() => { - return getContent(tab.dataset.tabClass, JSON.stringify(wsData)); + let tabArgs = {...tab.dataset}; + delete tabArgs.tabClass; + delete tabArgs.tabContent; + return getContent(tab.dataset.tabClass, JSON.stringify(tabArgs)); }) .then((data) => { tabjs = data.javascript; diff --git a/lib/classes/output/dynamic_tabs.php b/lib/classes/output/dynamic_tabs.php index 3a14323fc16..c2592bb8134 100644 --- a/lib/classes/output/dynamic_tabs.php +++ b/lib/classes/output/dynamic_tabs.php @@ -31,19 +31,15 @@ use templatable; */ class dynamic_tabs implements templatable { - /** @var array */ - protected $attributes; /** @var base[] */ protected $tabs = []; /** * tabs constructor. * - * @param array $attributes additional attributes that will be passed to the webservice * @param base[] $tabs array of tab */ - public function __construct(array $attributes = [], array $tabs = []) { - $this->attributes = $attributes; + public function __construct(array $tabs = []) { foreach ($tabs as $tab) { $this->add_tab($tab); } @@ -66,20 +62,21 @@ class dynamic_tabs implements templatable { */ public function export_for_template(renderer_base $output): array { $data = [ - 'dataattributes' => [], 'tabs' => [] ]; - foreach ($this->attributes as $name => $value) { - $data['dataattributes'][] = ['name' => $name, 'value' => $value]; - } - foreach ($this->tabs as $tab) { + $dataattributes = []; + foreach ($tab->get_data() as $name => $value) { + $dataattributes[] = ['name' => $name, 'value' => $value]; + } + $data['tabs'][] = [ 'shortname' => $tab->get_tab_id(), 'displayname' => $tab->get_tab_label(), 'enabled' => $tab->is_available(), - 'tabclass' => get_class($tab) + 'tabclass' => get_class($tab), + 'dataattributes' => $dataattributes, ]; } diff --git a/lib/classes/output/dynamic_tabs/base.php b/lib/classes/output/dynamic_tabs/base.php index 987ecd1ffab..2396ee645ec 100644 --- a/lib/classes/output/dynamic_tabs/base.php +++ b/lib/classes/output/dynamic_tabs/base.php @@ -83,4 +83,22 @@ abstract class base implements templatable { * @return string */ abstract public function get_template(): string; + + /** + * Return tab data attributes + * + * @return array + */ + public function get_data(): array { + return $this->data; + } + + /** + * Add custom data to the tab data attributes + * + * @param array $data + */ + public function add_data(array $data): void { + $this->data = array_merge($this->data, $data); + } } diff --git a/lib/templates/dynamic_tabs.mustache b/lib/templates/dynamic_tabs.mustache index 0c1cdb8a744..6f33a641d64 100644 --- a/lib/templates/dynamic_tabs.mustache +++ b/lib/templates/dynamic_tabs.mustache @@ -44,7 +44,7 @@ M.util.js_pending('core_dynamic_tabs_init'); -
+
{{#showtabsnavigation}}