2023-10-03 21:51:49 +00:00
|
|
|
const dropdownMenus = require( './dropdownMenus.js' );
|
|
|
|
|
2023-07-20 10:56:06 +00:00
|
|
|
/**
|
|
|
|
* An object containing the data to help create a portlet.
|
|
|
|
*
|
|
|
|
* @typedef {Object} Hint
|
|
|
|
* @property {string} type
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates default portlet.
|
|
|
|
*
|
2023-07-26 20:48:39 +00:00
|
|
|
* @param {Element} portlet
|
2023-10-03 21:51:49 +00:00
|
|
|
* @param {boolean} isDropdown
|
2023-07-26 20:48:39 +00:00
|
|
|
* @return {Element}
|
2023-07-20 10:56:06 +00:00
|
|
|
*/
|
2023-10-03 21:51:49 +00:00
|
|
|
function addDefaultPortlet( portlet, isDropdown ) {
|
2023-07-20 10:56:06 +00:00
|
|
|
const ul = portlet.querySelector( 'ul' );
|
|
|
|
if ( !ul ) {
|
|
|
|
return portlet;
|
|
|
|
}
|
|
|
|
ul.classList.add( 'vector-menu-content-list' );
|
|
|
|
const label = portlet.querySelector( 'label' );
|
|
|
|
if ( label ) {
|
|
|
|
const labelDiv = document.createElement( 'div' );
|
|
|
|
labelDiv.classList.add( 'vector-menu-heading' );
|
2023-10-03 21:51:49 +00:00
|
|
|
if ( !isDropdown ) {
|
2023-11-06 17:23:47 +00:00
|
|
|
labelDiv.innerHTML = label.textContent || '';
|
2023-10-03 21:51:49 +00:00
|
|
|
portlet.insertBefore( labelDiv, label );
|
|
|
|
label.remove();
|
|
|
|
}
|
2023-07-20 10:56:06 +00:00
|
|
|
}
|
|
|
|
let wrapper = portlet.querySelector( 'div:last-child' );
|
|
|
|
if ( wrapper ) {
|
|
|
|
ul.remove();
|
|
|
|
wrapper.appendChild( ul );
|
|
|
|
wrapper.classList.add( 'vector-menu-content' );
|
|
|
|
} else {
|
|
|
|
wrapper = document.createElement( 'div' );
|
|
|
|
wrapper.classList.add( 'vector-menu-content' );
|
|
|
|
ul.remove();
|
|
|
|
wrapper.appendChild( ul );
|
|
|
|
portlet.appendChild( wrapper );
|
|
|
|
}
|
|
|
|
portlet.classList.add( 'vector-menu' );
|
|
|
|
return portlet;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A hook handler for util.addPortlet hook.
|
|
|
|
* It creates a portlet based on the hint, and adabt it to vector skin.
|
|
|
|
*
|
2023-10-03 21:51:49 +00:00
|
|
|
* @param {Element} content
|
|
|
|
* @return {Element}
|
|
|
|
*/
|
|
|
|
function makeDropdown( content ) {
|
|
|
|
const id = content.id;
|
|
|
|
const label = content.querySelector( 'label' );
|
|
|
|
if ( !content.parentNode || !label ) {
|
|
|
|
return content;
|
|
|
|
}
|
2024-01-11 19:08:25 +00:00
|
|
|
label.id = `${ id }-dropdown-label`;
|
|
|
|
label.setAttribute( 'for', `${ id }-dropdown-checkbox` );
|
2023-10-03 21:51:49 +00:00
|
|
|
label.classList.add( 'vector-dropdown-label' );
|
|
|
|
label.setAttribute( 'aria-hidden', 'true' );
|
|
|
|
const labelSpan = document.createElement( 'span' );
|
|
|
|
labelSpan.textContent = label.textContent;
|
|
|
|
label.textContent = '';
|
|
|
|
labelSpan.classList.add( 'vector-dropdown-label-text' );
|
|
|
|
label.appendChild( labelSpan );
|
|
|
|
const dropdown = document.createElement( 'div' );
|
|
|
|
const checkbox = document.createElement( 'input' );
|
|
|
|
const dropdownContent = document.createElement( 'div' );
|
|
|
|
dropdownContent.classList.add( 'vector-dropdown-content' );
|
|
|
|
checkbox.type = 'checkbox';
|
2024-01-11 19:08:25 +00:00
|
|
|
checkbox.id = `${ id }-dropdown-checkbox`;
|
2023-10-03 21:51:49 +00:00
|
|
|
checkbox.setAttribute( 'role', 'button' );
|
|
|
|
checkbox.setAttribute( 'aria-haspopup', 'true' );
|
2024-01-11 19:08:25 +00:00
|
|
|
checkbox.setAttribute( 'data-event-name', `ui.dropdown-${ id }-dropdown` );
|
2023-10-03 21:51:49 +00:00
|
|
|
checkbox.classList.add( 'vector-dropdown-checkbox' );
|
2023-11-06 17:23:47 +00:00
|
|
|
checkbox.setAttribute( 'aria-label', label.textContent || '' );
|
2024-01-11 19:08:25 +00:00
|
|
|
dropdown.id = `${ id }-dropdown`;
|
|
|
|
dropdown.classList.add( 'vector-dropdown', `${ id }-dropdown` );
|
2023-10-03 21:51:49 +00:00
|
|
|
dropdown.appendChild( checkbox );
|
|
|
|
dropdown.appendChild( label );
|
|
|
|
dropdown.appendChild( dropdownContent );
|
|
|
|
content.parentNode.insertBefore( dropdown, content );
|
|
|
|
dropdownContent.appendChild( content );
|
|
|
|
dropdownMenus.dropdownMenus( [ dropdown ] );
|
|
|
|
return dropdown;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A hook handler for util.addPortlet hook.
|
|
|
|
* It creates a portlet based on the hint, and adapt it to vector skin.
|
|
|
|
* If #p-cactions is used, the new portlet will be converted into a dropdown.
|
|
|
|
*
|
2023-07-26 20:48:39 +00:00
|
|
|
* @param {Element} portlet
|
2023-10-03 21:51:49 +00:00
|
|
|
* @param {string|null} before
|
2023-07-26 20:48:39 +00:00
|
|
|
* @return {Element}
|
2023-07-20 10:56:06 +00:00
|
|
|
*/
|
2023-10-03 21:51:49 +00:00
|
|
|
function addPortletHandler( portlet, before ) {
|
|
|
|
|
|
|
|
const isDropdown = !!( before && before === '#p-cactions' );
|
2023-07-20 10:56:06 +00:00
|
|
|
portlet.classList.remove( 'mw-portlet-js' );
|
2023-10-03 21:51:49 +00:00
|
|
|
|
|
|
|
const transformedPortlet = addDefaultPortlet( portlet, isDropdown );
|
|
|
|
if ( isDropdown ) {
|
|
|
|
const pageToolsDropdown = document.querySelector( '#vector-page-tools-dropdown' );
|
|
|
|
const pageToolsMarker = pageToolsDropdown ? pageToolsDropdown.parentNode : null;
|
|
|
|
// Guard against unexpected changes to HTML.
|
|
|
|
if ( pageToolsMarker === null || !pageToolsMarker.parentNode ) {
|
2023-11-06 17:23:47 +00:00
|
|
|
throw new Error( 'Vector 2022 addPortletLink: No #vector-page-tools-dropdown element in the DOM.' );
|
2023-10-03 21:51:49 +00:00
|
|
|
}
|
|
|
|
const dropdown = makeDropdown( transformedPortlet );
|
|
|
|
pageToolsMarker.parentNode.insertBefore( dropdown, pageToolsMarker );
|
|
|
|
return transformedPortlet;
|
|
|
|
}
|
|
|
|
return transformedPortlet;
|
2023-07-20 10:56:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2023-10-03 21:51:49 +00:00
|
|
|
* @return {{addPortletHandler: (function(Element, string): Element)}}
|
2023-07-20 10:56:06 +00:00
|
|
|
*/
|
|
|
|
function main() {
|
2023-07-26 20:48:39 +00:00
|
|
|
mw.hook( 'util.addPortlet' ).add( addPortletHandler );
|
|
|
|
// Update any portlets that were created prior to the hook being registered.
|
2023-10-03 21:51:49 +00:00
|
|
|
document.querySelectorAll( '.mw-portlet-js' ).forEach( ( node ) => {
|
2023-11-06 17:23:47 +00:00
|
|
|
const nextID = node && node.nextElementSibling && node.nextElementSibling.id;
|
2024-01-11 19:08:25 +00:00
|
|
|
addPortletHandler( node, nextID ? `#${ nextID }` : null );
|
2023-10-03 21:51:49 +00:00
|
|
|
} );
|
2023-07-20 10:56:06 +00:00
|
|
|
return {
|
|
|
|
addPortletHandler
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = {
|
|
|
|
main, addPortletHandler
|
|
|
|
};
|