mirror of
https://github.com/StarCitizenTools/mediawiki-skins-Citizen.git
synced 2024-12-11 14:06:33 +00:00
152 lines
4.4 KiB
JavaScript
152 lines
4.4 KiB
JavaScript
/**
|
|
* Enhance dropdown menus
|
|
* Based on Vector
|
|
*/
|
|
const
|
|
DROPDOWN_CONTAINER_SELECTOR = '.citizen-dropdown',
|
|
DROPDOWN_DETAILS_SELECTOR = '.citizen-dropdown-details',
|
|
DROPDOWN_SUMMARY_SELECTOR = '.citizen-dropdown-summary',
|
|
DROPDOWN_TARGET_SELECTOR = '.citizen-menu__card';
|
|
|
|
const isPointerDevice = window.matchMedia( '(hover: hover) and (pointer: fine)' ).matches;
|
|
|
|
/**
|
|
* Represents a Dropdown menu with enhanced functionality.
|
|
* This class handles the behavior of a dropdown menu, including dismissing the menu when clicking outside,
|
|
* pressing the ESCAPE key, losing focus, or clicking on links.
|
|
* It provides methods to bind and unbind event listeners for different actions.
|
|
* The 'init' method initializes the dropdown menu by adding necessary event listeners.
|
|
*
|
|
* @class
|
|
*/
|
|
class Dropdown {
|
|
constructor( details, summary, target ) {
|
|
this.details = details;
|
|
this.summary = summary;
|
|
this.target = target;
|
|
this.dismissOnEscape = this.dismissOnEscape.bind( this );
|
|
this.dismissIfExternalEventTarget = this.dismissIfExternalEventTarget.bind( this );
|
|
this.dismissOnLinkClick = this.dismissOnLinkClick.bind( this );
|
|
this.onDetailsToggle = this.onDetailsToggle.bind( this );
|
|
}
|
|
|
|
dismiss() {
|
|
if ( this.details && this.details.open ) {
|
|
this.details.open = false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Dismiss the target when ESCAPE is pressed.
|
|
*
|
|
* @param {Event} event
|
|
*/
|
|
dismissOnEscape( event ) {
|
|
if ( event.key === 'Escape' ) {
|
|
this.dismiss();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Dismiss the target when event is outside the target and summary
|
|
*
|
|
* @param {Event} event
|
|
*/
|
|
dismissIfExternalEventTarget( event ) {
|
|
if ( !this.target.contains( event.target ) && !this.summary.contains( event.target ) ) {
|
|
this.dismiss();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Dismiss the target on clicks to links and link children elements
|
|
*
|
|
* @param {Event} event
|
|
*/
|
|
dismissOnLinkClick( event ) {
|
|
const eventTarget = event.target;
|
|
if ( eventTarget && eventTarget.closest( 'a' ) ) {
|
|
this.dismiss();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Unbind event listeners for the dropdown menu.
|
|
*/
|
|
unbind() {
|
|
this.target.removeEventListener( 'click', this.dismissOnLinkClick );
|
|
window.removeEventListener( 'mousedown', this.dismissIfExternalEventTarget );
|
|
window.removeEventListener( 'touchstart', this.dismissIfExternalEventTarget );
|
|
window.removeEventListener( 'focusin', this.dismissIfExternalEventTarget );
|
|
window.removeEventListener( 'keyup', this.dismissOnEscape );
|
|
}
|
|
|
|
/**
|
|
* Bind event listeners for the dropdown menu.
|
|
*/
|
|
bind() {
|
|
this.target.addEventListener( 'click', this.dismissOnLinkClick );
|
|
window.addEventListener( 'mousedown', this.dismissIfExternalEventTarget );
|
|
window.addEventListener( 'touchstart', this.dismissIfExternalEventTarget, { passive: true } );
|
|
window.addEventListener( 'focusin', this.dismissIfExternalEventTarget );
|
|
window.addEventListener( 'keyup', this.dismissOnEscape );
|
|
}
|
|
|
|
onDetailsToggle() {
|
|
if ( this.details.open ) {
|
|
this.bind();
|
|
} else {
|
|
this.unbind();
|
|
}
|
|
}
|
|
|
|
addKeyhint() {
|
|
if ( !isPointerDevice ) {
|
|
return;
|
|
}
|
|
|
|
const links = this.target.querySelectorAll( '.mw-list-item > a[accesskey]' );
|
|
links.forEach( ( link ) => {
|
|
const keyhintText = window.jQuery.fn.updateTooltipAccessKeys.getAccessKeyPrefix() + link.getAttribute( 'accesskey' );
|
|
if ( !keyhintText ) {
|
|
return;
|
|
}
|
|
const keyhint = document.createElement( 'span' );
|
|
keyhint.classList.add( 'citizen-keyboard-hint-key' );
|
|
// There isn't any shortcut using '-' as accesskey so we should be good
|
|
// @see https://www.mediawiki.org/wiki/Help:Keyboard_shortcuts
|
|
keyhint.innerText = keyhintText.replace( /-/g, ' ' );
|
|
link.append( keyhint );
|
|
} );
|
|
}
|
|
|
|
init() {
|
|
this.details.addEventListener( 'toggle', this.onDetailsToggle );
|
|
// T295085: Close all dropdown menus when page is unloaded to prevent them
|
|
// from being open when navigating back to a page.
|
|
window.addEventListener( 'beforeunload', () => this.dismiss );
|
|
this.addKeyhint();
|
|
}
|
|
}
|
|
|
|
function init() {
|
|
const dropdowns = document.querySelectorAll( DROPDOWN_CONTAINER_SELECTOR );
|
|
|
|
dropdowns.forEach( ( dropdown ) => {
|
|
const
|
|
details = dropdown.querySelector( DROPDOWN_DETAILS_SELECTOR ),
|
|
summary = dropdown.querySelector( DROPDOWN_SUMMARY_SELECTOR ),
|
|
target = dropdown.querySelector( DROPDOWN_TARGET_SELECTOR );
|
|
|
|
if ( !( details && summary && target ) ) {
|
|
return;
|
|
}
|
|
|
|
new Dropdown( details, summary, target ).init();
|
|
} );
|
|
}
|
|
|
|
module.exports = {
|
|
init: init
|
|
};
|