/**
* @file menu.js
*/
import Component from '../component.js' ;
import document from 'global/document' ;
import * as Dom from '../utils/dom.js' ;
import * as Events from '../utils/events.js' ;
import keycode from 'keycode' ;
/**
* Le composant Menu est utilisé pour construire des menus contextuels, y compris des menus de sous-titres et de
* menus de sélection des sous-titres.
*
* @extends Component
*/
class Menu extends Component {
/**
* Créer une instance de cette classe.
*
* @param {Player} player
* le lecteur auquel ce composant doit s'attacher
*
* @param {Objet} [options]
* Objet de noms et de valeurs d'options
*
*/
constructor(player, options) {
super(player, options) ;
if (options) {
this.menuButton_ = options.menuButton ;
}
this.focusedChild_ = -1 ;
this.on('keydown', (e) => this.handleKeyDown(e)) ;
// Toutes les instances d'éléments de menu partagent le même gestionnaire de flou fourni par le conteneur de menu.
this.boundHandleBlur_ = (e) => this.handleBlur(e) ;
this.boundHandleTapClick_ = (e) => this.handleTapClick(e) ;
}
/**
* Ajouter des récepteurs d'événements à l'élément {@link MenuItem}.
*
* @param {Object} component
* L'instance du `MenuItem` à laquelle ajouter des listeners.
*
*/
addEventListenerForItem(component) {
if ( !(component instanceof Component)) {
retour ;
}
this.on(component, 'blur', this.boundHandleBlur_) ;
this.on(component, ['tap', 'click'], this.boundHandleTapClick_) ;
}
/**
* Supprime les récepteurs d'événements de l'élément {@link MenuItem}.
*
* @param {Object} component
* L'instance du `MenuItem` à supprimer des listeners.
*
*/
removeEventListenerForItem(component) {
if ( !(component instanceof Component)) {
retour ;
}
this.off(component, 'blur', this.boundHandleBlur_) ;
this.off(component, ['tap', 'click'], this.boundHandleTapClick_) ;
}
/**
* Cette méthode sera appelée indirectement lorsque le composant aura été ajouté
* avant que le composant ne s'ajoute à la nouvelle instance de menu par `addItem`.
* Dans ce cas, l'instance de menu d'origine supprimera le composant
* en appelant `removeChild`.
*
* @param {Object} component
* L'instance de l'élément de menu `MenuItem`
*/
removeChild(component) {
if (typeof component === 'string') {
component = this.getChild(component) ;
}
this.removeEventListenerForItem(component) ;
super.removeChild(component) ;
}
/**
* Ajoute un {@link MenuItem} au menu.
*
* @param {Objet|chaîne} composant
* Le nom ou l'instance du `MenuItem` à ajouter.
*
*/
addItem(component) {
const childComponent = this.addChild(component) ;
if (childComponent) {
this.addEventListenerForItem(childComponent) ;
}
}
/**
* Créer l'élément DOM `Menu`.
*
* @return {Element}
* l'élément qui a été créé
*/
createEl() {
const contentElType = this.options_.contentElType || 'ul' ;
this.contentEl_ = Dom.createEl(contentElType, {
className : 'vjs-menu-content'
}) ;
this.contentEl_.setAttribute('role', 'menu') ;
const el = super.createEl('div', {
append : this.contentEl_,
className : 'vjs-menu'
}) ;
el.appendChild(this.contentEl_) ;
// Empêcher les clics de remonter. Nécessaire pour les boutons de menu,
// lorsqu'un clic sur le parent est significatif
Events.on(el, 'click', function(event) {
event.preventDefault() ;
event.stopImmediatePropagation() ;
}) ;
return el ;
}
dispose() {
this.contentEl_ = null ;
this.boundHandleBlur_ = null ;
this.boundHandleTapClick_ = null ;
super.dispose() ;
}
/**
* Appelé lorsqu'un `MenuItem` perd le focus.
*
* @param {EventTarget~Event} event
* L'événement `blur` qui a provoqué l'appel de cette fonction.
*
* @listens blur
*/
handleBlur(event) {
const relatedTarget = event.relatedTarget || document.activeElement ;
// Fermer le menu contextuel lorsque l'utilisateur clique en dehors du menu
if (!this.children().some((element) => {
return element.el() === relatedTarget ;
})) {
const btn = this.menuButton_ ;
if (btn && btn.buttonPressed_ && relatedTarget !== btn.el().firstChild) {
btn.unpressButton() ;
}
}
}
/**
* Appelé lorsqu'un `MenuItem` est cliqué ou tapé.
*
* @param {EventTarget~Event} event
* L'événement `click` ou `tap` qui a provoqué l'appel de cette fonction.
*
* @listens click,tap
*/
handleTapClick(event) {
// Décrocher le bouton de menu associé et lui redonner la priorité
if (this.menuButton_) {
this.menuButton_.unpressButton() ;
const childComponents = this.children() ;
if (!Array.isArray(childComponents)) {
retour ;
}
const foundComponent = childComponents.filter(component => component.el() === event.target)[0] ;
if (!foundComponent) {
retour ;
}
// ne pas mettre en évidence le bouton de menu si l'élément est un élément de réglage de légende
// parce que le centre d'intérêt se déplacera ailleurs
if (foundComponent.name() !== 'CaptionSettingsMenuItem') {
this.menuButton_.focus() ;
}
}
}
/**
* Gère un événement `keydown` sur ce menu. Cet écouteur est ajouté dans le constructeur.
*
* @param {EventTarget~Event} event
* Un événement `keydown` qui s'est produit dans le menu.
*
* @listens keydown
*/
handleKeyDown(event) {
// Flèches de gauche et de bas
if (keycode.isEventKey(event, 'Left') || keycode.isEventKey(event, 'Down')) {
event.preventDefault() ;
event.stopPropagation() ;
this.stepForward() ;
// Flèches vers le haut et vers la droite
} else if (keycode.isEventKey(event, 'Right') || keycode.isEventKey(event, 'Up')) {
event.preventDefault() ;
event.stopPropagation() ;
this.stepBack() ;
}
}
/**
* Permet de passer à l'élément de menu suivant (inférieur) pour les utilisateurs du clavier.
*/
stepForward() {
let stepChild = 0 ;
if (this.focusedChild_ !== undefined) {
stepChild = this.focusedChild_ + 1 ;
}
this.focus(stepChild) ;
}
/**
* Permet de passer à l'élément de menu précédent (supérieur) pour les utilisateurs du clavier.
*/
stepBack() {
let stepChild = 0 ;
if (this.focusedChild_ !== undefined) {
stepChild = this.focusedChild_ - 1 ;
}
this.focus(stepChild) ;
}
/**
* Mettre le focus sur un {@link MenuItem} dans le `Menu`.
*
* @param {Objet|chaîne} [item=0]
* Index de l'élément enfant sur lequel l'accent est mis.
*/
focus(item = 0) {
const children = this.children().slice() ;
const haveTitle = children.length && children[0].hasClass('vjs-menu-title') ;
if (haveTitle) {
enfants.shift() ;
}
if (children.length > 0) {
if (item < 0) {
item = 0 ;
} else if (item >= children.length) {
item = children.length - 1 ;
}
this.focusedChild_ = item ;
children[item].el_.focus() ;
}
}
}
Component.registerComponent('Menu', Menu) ;
exporter le menu par défaut ;