Proyectos de Subversion Moodle

Rev

Autoría | Ultima modificación | Ver Log |

{"version":3,"file":"moremenu.min.js","sources":["../src/moremenu.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 <http://www.gnu.org/licenses/>.\n\n/**\n * Moves wrapping navigation items into a more menu.\n *\n * @module     core/moremenu\n * @copyright  2021 Moodle\n * @author     Bas Brands <bas@moodle.com>\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport menu_navigation from \"core/menu_navigation\";\n/**\n * Moremenu selectors.\n */\nconst Selectors = {\n    regions: {\n        moredropdown: '[data-region=\"moredropdown\"]',\n        morebutton: '[data-region=\"morebutton\"]'\n    },\n    classes: {\n        dropdownitem: 'dropdown-item',\n        dropdownmoremenu: 'dropdownmoremenu',\n        hidden: 'd-none',\n        active: 'active',\n        nav: 'nav',\n        navlink: 'nav-link',\n        observed: 'observed',\n    },\n    attributes: {\n        menu: '[role=\"menu\"]',\n        dropdowntoggle: '[data-toggle=\"dropdown\"]'\n    }\n};\n\nlet isTabListMenu = false;\n\n/**\n * Auto Collapse navigation items that wrap into a dropdown menu.\n *\n * @param {HTMLElement} menu The navbar container.\n */\nconst autoCollapse = menu => {\n\n    const maxHeight = menu.parentNode.offsetHeight + 1;\n\n    const moreDropdown = menu.querySelector(Selectors.regions.moredropdown);\n    const moreButton = menu.querySelector(Selectors.regions.morebutton);\n\n    // If the menu items wrap and the menu height is larger than the height of the\n    // parent then start pushing navlinks into the moreDropdown.\n    if (menu.offsetHeight > maxHeight) {\n        moreButton.classList.remove(Selectors.classes.hidden);\n\n        let menuHeight = 0;\n        const menuNodes = Array.from(menu.children).reverse();\n        menuNodes.forEach(item => {\n            if (!item.classList.contains(Selectors.classes.dropdownmoremenu)) {\n                // After moving the menu items into the moreDropdown check again\n                // if the menu height is still larger then the height of the parent.\n                if (menu.offsetHeight > maxHeight) {\n                    // Move this node into the more dropdown menu.\n                    moveIntoMoreDropdown(menu, item, true);\n                } else if (menuHeight > maxHeight) {\n                    moveIntoMoreDropdown(menu, item, true);\n                    menuHeight = 0;\n                }\n            } else if (menu.offsetHeight > maxHeight) {\n                // Assign menu height to be used to check with menu parent.\n                menuHeight = menu.offsetHeight;\n            }\n        });\n    } else {\n        // If the menu height is smaller than the height of the parent, then try returning navlinks to the menu.\n        if ('children' in moreDropdown) {\n            // Iterate through the nodes within the more dropdown menu.\n            Array.from(moreDropdown.children).forEach(item => {\n                // Don't move the node to the more menu if it is explicitly defined that\n                // this node should be displayed in the more dropdown menu at all times.\n                if (menu.offsetHeight < maxHeight && item.dataset.forceintomoremenu !== 'true') {\n                    const lastNode = moreDropdown.removeChild(item);\n                    // Move this node from the more dropdown menu into the main section of the menu.\n                    moveOutOfMoreDropdown(menu, lastNode);\n                }\n            });\n            // If there are no more nodes in the more dropdown menu we can hide the moreButton.\n            if (Array.from(moreDropdown.children).length === 0) {\n                moreButton.classList.add(Selectors.classes.hidden);\n            }\n        }\n\n        if (menu.offsetHeight > maxHeight) {\n            autoCollapse(menu);\n        }\n    }\n    menu.parentNode.classList.add(Selectors.classes.observed);\n};\n\n/**\n * Move a node into the \"more\" dropdown menu.\n *\n * This method forces a given navigation node to be added and displayed within the \"more\" dropdown menu.\n *\n * @param {HTMLElement} menu The navbar moremenu.\n * @param {HTMLElement} navNode The navigation node.\n * @param {boolean} prepend Whether to prepend or append the node to the content in the more dropdown menu.\n */\nconst moveIntoMoreDropdown = (menu, navNode, prepend = false) => {\n    const moreDropdown = menu.querySelector(Selectors.regions.moredropdown);\n    const dropdownToggle = menu.querySelector(Selectors.attributes.dropdowntoggle);\n\n    const navLink = navNode.querySelector('.' + Selectors.classes.navlink);\n    // If there are navLinks that contain an active link in the moreDropdown\n    // make the dropdownToggle in the moreButton active.\n    if (navLink.classList.contains(Selectors.classes.active)) {\n        dropdownToggle.classList.add(Selectors.classes.active);\n        dropdownToggle.setAttribute('tabindex', '0');\n        navLink.setAttribute('tabindex', '-1'); // So that we don't have a single tabbable menu item.\n        // Remove aria-selected if the more menu is rendered as a tab list.\n        if (isTabListMenu) {\n            navLink.removeAttribute('aria-selected');\n        }\n        navLink.setAttribute('aria-current', 'true');\n    }\n\n    // This will become a menu item instead of a tab.\n    navLink.setAttribute('role', 'menuitem');\n\n    // Change the styling of the navLink to a dropdownitem and push it into\n    // the moreDropdown.\n    navLink.classList.remove(Selectors.classes.navlink);\n    navLink.classList.add(Selectors.classes.dropdownitem);\n    if (prepend) {\n        moreDropdown.prepend(navNode);\n    } else {\n        moreDropdown.append(navNode);\n    }\n};\n\n/**\n * Move a node out of the \"more\" dropdown menu.\n *\n * This method forces a given node from the \"more\" dropdown menu to be displayed in the main section of the menu.\n *\n * @param {HTMLElement} menu The navbar moremenu.\n * @param {HTMLElement} navNode The navigation node.\n */\nconst moveOutOfMoreDropdown = (menu, navNode) => {\n    const moreButton = menu.querySelector(Selectors.regions.morebutton);\n    const dropdownToggle = menu.querySelector(Selectors.attributes.dropdowntoggle);\n    const navLink = navNode.querySelector('.' + Selectors.classes.dropdownitem);\n\n    // If the more menu is rendered as a tab list,\n    // this will become a tab instead of a menuitem when moved out of the more menu dropdown.\n    if (isTabListMenu) {\n        navLink.setAttribute('role', 'tab');\n    }\n\n    // Stop displaying the active state on the dropdownToggle if\n    // the active navlink is removed.\n    if (navLink.classList.contains(Selectors.classes.active)) {\n        dropdownToggle.classList.remove(Selectors.classes.active);\n        dropdownToggle.setAttribute('tabindex', '-1');\n        navLink.setAttribute('tabindex', '0');\n        if (isTabListMenu) {\n            // Replace aria selection state when necessary.\n            navLink.removeAttribute('aria-current');\n            navLink.setAttribute('aria-selected', 'true');\n        }\n    }\n    navLink.classList.remove(Selectors.classes.dropdownitem);\n    navLink.classList.add(Selectors.classes.navlink);\n    menu.insertBefore(navNode, moreButton);\n};\n\n/**\n * Initialise the more menus.\n *\n * @param {HTMLElement} menu The navbar moremenu.\n */\nexport default menu => {\n    isTabListMenu = menu.getAttribute('role') === 'tablist';\n\n    // Select the first menu item if there's nothing initially selected.\n    const hash = window.location.hash;\n    if (!hash) {\n        const itemRole = isTabListMenu ? 'tab' : 'menuitem';\n        const menuListItem = menu.firstElementChild;\n        const roleSelector = `[role=${itemRole}]`;\n        const menuItem = menuListItem.querySelector(roleSelector);\n        const ariaAttribute = isTabListMenu ? 'aria-selected' : 'aria-current';\n        if (!menu.querySelector(`[${ariaAttribute}='true']`)) {\n            menuItem.setAttribute(ariaAttribute, 'true');\n            menuItem.setAttribute('tabindex', '0');\n        }\n    }\n\n    // Pre-populate the \"more\" dropdown menu with navigation nodes which are set to be displayed in this menu\n    // by default at all times.\n    if ('children' in menu) {\n        const moreButton = menu.querySelector(Selectors.regions.morebutton);\n        const menuNodes = Array.from(menu.children);\n        menuNodes.forEach((item) => {\n            if (!item.classList.contains(Selectors.classes.dropdownmoremenu) &&\n                    item.dataset.forceintomoremenu === 'true') {\n                // Append this node into the more dropdown menu.\n                moveIntoMoreDropdown(menu, item, false);\n                // After adding the node into the more dropdown menu, make sure that the more dropdown menu button\n                // is displayed.\n                if (moreButton.classList.contains(Selectors.classes.hidden)) {\n                    moreButton.classList.remove(Selectors.classes.hidden);\n                }\n            }\n        });\n    }\n    // Populate the more dropdown menu with additional nodes if necessary, depending on the current screen size.\n    autoCollapse(menu);\n    menu_navigation(menu);\n\n    // When the screen size changes make sure the menu still fits.\n    window.addEventListener('resize', () => {\n        autoCollapse(menu);\n        menu_navigation(menu);\n    });\n\n    const toggledropdown = e => {\n        const innerMenu = e.target.parentNode.querySelector(Selectors.attributes.menu);\n        if (innerMenu) {\n            innerMenu.classList.toggle('show');\n        }\n        e.stopPropagation();\n    };\n\n    // If there are dropdowns in the MoreMenu, add a new\n    // event listener to show the contents on click and prevent the\n    // moreMenu from closing.\n    $('.' + Selectors.classes.dropdownmoremenu).on('show.bs.dropdown', function() {\n        const moreDropdown = menu.querySelector(Selectors.regions.moredropdown);\n        moreDropdown.querySelectorAll('.dropdown').forEach((dropdown) => {\n            dropdown.removeEventListener('click', toggledropdown, true);\n            dropdown.addEventListener('click', toggledropdown, true);\n        });\n    });\n};\n"],"names":["Selectors","moredropdown","morebutton","dropdownitem","dropdownmoremenu","hidden","active","nav","navlink","observed","menu","dropdowntoggle","isTabListMenu","autoCollapse","maxHeight","parentNode","offsetHeight","moreDropdown","querySelector","moreButton","classList","remove","menuHeight","Array","from","children","reverse","forEach","item","contains","moveIntoMoreDropdown","dataset","forceintomoremenu","lastNode","removeChild","moveOutOfMoreDropdown","length","add","navNode","prepend","dropdownToggle","navLink","setAttribute","removeAttribute","append","insertBefore","getAttribute","window","location","hash","itemRole","menuListItem","firstElementChild","roleSelector","menuItem","ariaAttribute","addEventListener","toggledropdown","e","innerMenu","target","toggle","stopPropagation","on","querySelectorAll","dropdown","removeEventListener"],"mappings":"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"}