mirror of
https://0xacab.org/sutty/sutty
synced 2024-11-24 15:46:23 +00:00
107 lines
2.8 KiB
JavaScript
107 lines
2.8 KiB
JavaScript
|
import { Controller } from "stimulus";
|
||
|
|
||
|
// https://getbootstrap.com/docs/4.6/components/dropdowns/#single-button
|
||
|
export default class extends Controller {
|
||
|
static targets = ["dropdown", "button", "item"];
|
||
|
|
||
|
// Al iniciar el controlador
|
||
|
connect() {
|
||
|
// Llevar la cuenta del item con foco
|
||
|
this.data.set("item", -1);
|
||
|
|
||
|
// Gestionar las teclas
|
||
|
this.keydownEvent = this.keydown.bind(this);
|
||
|
this.element.addEventListener("keydown", this.keydownEvent);
|
||
|
|
||
|
// Gestionar el foco
|
||
|
this.focusinEvent = this.focusin.bind(this);
|
||
|
}
|
||
|
|
||
|
// Al eliminar el controlador (al pasar a otra página)
|
||
|
disconnect() {
|
||
|
// Eliminar la gestión de teclas
|
||
|
this.element.removeEventListener("keydown", this.keydownEvent);
|
||
|
// Eliminar la gestión del foco
|
||
|
document.removeEventListener("focusin", this.focusinEvent);
|
||
|
}
|
||
|
|
||
|
// Mostrar u ocultar
|
||
|
toggle(event) {
|
||
|
(this.buttonTarget.ariaExpanded === "false") ? this.show() : this.hide();
|
||
|
}
|
||
|
|
||
|
// Mostrar
|
||
|
show() {
|
||
|
this.buttonTarget.ariaExpanded = "true";
|
||
|
this.element.classList.add("show");
|
||
|
this.dropdownTarget.classList.add("show");
|
||
|
|
||
|
// Activar la gestión del foco
|
||
|
document.addEventListener("focusin", this.focusinEvent);
|
||
|
}
|
||
|
|
||
|
// Ocultar
|
||
|
hide() {
|
||
|
this.buttonTarget.ariaExpanded = "false";
|
||
|
this.element.classList.remove("show");
|
||
|
this.dropdownTarget.classList.remove("show");
|
||
|
// Volver al inicio el foco de items
|
||
|
this.data.set("item", -1);
|
||
|
|
||
|
// Desactivar la gestión del foco
|
||
|
document.removeEventListener("focusin", this.focusinEvent);
|
||
|
}
|
||
|
|
||
|
// Gestionar el foco
|
||
|
focusin(event) {
|
||
|
const item = this.itemTargets.find(x => x === event.target);
|
||
|
|
||
|
// Si el foco se coloca sobre elementos del controlador, no hacer
|
||
|
// nada
|
||
|
if (event.target === this.buttonTarget || item) {
|
||
|
// Si es un item, el comportamiento de las flechas verticales y el
|
||
|
// Tab tiene que ser igual
|
||
|
if (item) this.data.set("item", this.itemTargets.indexOf(item));
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// De lo contrario, ocultar
|
||
|
this.hide();
|
||
|
}
|
||
|
|
||
|
// Gestionar las teclas
|
||
|
keydown(event) {
|
||
|
const initial = parseInt(this.data.get("item"));
|
||
|
let item = initial;
|
||
|
|
||
|
switch (event.keyCode) {
|
||
|
case 27:
|
||
|
// Esc cierra el menú y devuelve el foco
|
||
|
this.hide();
|
||
|
this.buttonTarget.focus();
|
||
|
break;
|
||
|
case 38:
|
||
|
// Moverse hacia arriba con tope en el primer item
|
||
|
if (item > -1) item--;
|
||
|
|
||
|
break;
|
||
|
case 40:
|
||
|
// Moverse hacia abajo con tope en el último ítem, si el
|
||
|
// dropdown estaba cerrado, abrirlo.
|
||
|
if (item === -1) this.show();
|
||
|
if (item <= this.itemTargets.length) item++;
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// Si cambió la posición del ítem, darle foco y actualizar el
|
||
|
// contador.
|
||
|
if (initial !== item) {
|
||
|
this.itemTargets[item]?.focus();
|
||
|
|
||
|
this.data.set("item", item);
|
||
|
}
|
||
|
}
|
||
|
}
|