mirror of
https://0xacab.org/sutty/sutty
synced 2024-11-24 15:26:23 +00:00
Nulo
6a7511a5e1
Utilizaba https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoViewIfNeeded que es propietario. Ahora usa scrollIntoView siempre, configurado para que prácticamente siempre se pueda ver el post al reordenar.
210 lines
4.7 KiB
JavaScript
210 lines
4.7 KiB
JavaScript
import { Controller } from 'stimulus'
|
|
|
|
/*
|
|
* Permite reordenar las filas de una tabla.
|
|
*
|
|
* Cada fila tiene un selector que permite decidir si la fila se
|
|
* mantiene en su lugar o se mueve al presionar las teclas de subir y
|
|
* bajar.
|
|
*
|
|
* Se pueden mover varias juntas.
|
|
*
|
|
* El controlador está en la tabla y cada fila es un objetivo. Dentro
|
|
* de cada fila tiene que haber un input[type=checkbox] que determina si
|
|
* está seleccionada o no y un input[type=hidden] que contiene la
|
|
* posición actual que luego será guardada.
|
|
*
|
|
* La tabla tiene que estar rodeada de un formulario para poder enviar
|
|
* los datos.
|
|
*
|
|
* El objetivo es poder mover filas en tablas de miles de elementos.
|
|
*/
|
|
export default class extends Controller {
|
|
static targets = [ 'row', 'counter' ]
|
|
|
|
connect () {
|
|
// Lo asociamos al documento porque en la tabla se pierde el foco
|
|
// luego del primer evento.
|
|
document.addEventListener('keydown', event => {
|
|
if (!this.codes.includes(event.keyCode)) return
|
|
if (this.empty) return
|
|
|
|
event.preventDefault()
|
|
|
|
this.move(event.keyCode === 38 ? 'up' : 'down')
|
|
})
|
|
}
|
|
|
|
get selected_rows () {
|
|
if (!this._selected_rows) this._selected_rows = {}
|
|
|
|
return this._selected_rows
|
|
}
|
|
|
|
// Arriba, abajo
|
|
get codes () {
|
|
if (!this._codes) this._codes = [ 38, 40 ]
|
|
|
|
return this._codes
|
|
}
|
|
|
|
get empty () {
|
|
return (Object.keys(this.selected_rows).length === 0)
|
|
}
|
|
|
|
/*
|
|
* Las filas siempre ordenadas
|
|
*/
|
|
get sorted_rows () {
|
|
return Object.values(this.selected_rows).sort((a,b) => a.order - b.order)
|
|
}
|
|
|
|
/*
|
|
* Aplica el nuevo orden en las filas y sus campos
|
|
*/
|
|
reorder () {
|
|
for (const r of Object.values(this.selected_rows)) {
|
|
this.selected_rows[r.row.id].order = this.rowTargets.indexOf(r.row)
|
|
}
|
|
|
|
const length = this.rowTargets.length
|
|
|
|
this.rowTargets.forEach((row, i) => {
|
|
row.querySelector('[data-reorder]').value = length - i
|
|
})
|
|
}
|
|
|
|
move (direction) {
|
|
if (this.empty) return
|
|
|
|
const up = direction === 'up'
|
|
const down = !up
|
|
const direction_sibling = up ? 'previousElementSibling' : 'nextElementSibling'
|
|
|
|
// Los movemos en orden
|
|
const rows = this.sorted_rows
|
|
if (down) rows.reverse()
|
|
|
|
for (const r of rows) {
|
|
const row = r.row
|
|
const sibling = row[direction_sibling]
|
|
|
|
// Estamos en el tope?
|
|
if (!sibling || sibling.tagName !== row.tagName) continue
|
|
|
|
if (up) {
|
|
row.parentElement.insertBefore(row, sibling)
|
|
} else {
|
|
row.parentElement.insertBefore(sibling, row)
|
|
}
|
|
}
|
|
|
|
// Reacomodamos el orden
|
|
this.reorder()
|
|
|
|
// Mantenemos el primero a la vista
|
|
rows[0].row.scrollIntoView({ block: "center" });
|
|
}
|
|
|
|
counter () {
|
|
this.counterTarget.innerText = Object.keys(this.selected_rows).length
|
|
}
|
|
|
|
// Deseleccionar todos
|
|
unselect (event) {
|
|
event.preventDefault()
|
|
event.stopPropagation()
|
|
|
|
for (const r of Object.values(this.selected_rows)) {
|
|
r.row.querySelector('[data-action="reorder#select"]').click()
|
|
}
|
|
}
|
|
|
|
// Enviar arriba de todo
|
|
top (event) {
|
|
event.preventDefault()
|
|
event.stopPropagation()
|
|
|
|
if (this.empty) return
|
|
|
|
const rows = this.sorted_rows
|
|
const first = rows[0].row.parentElement.firstElementChild
|
|
|
|
for (const r of rows) {
|
|
const row = r.row
|
|
|
|
if (row === first) continue
|
|
|
|
row.parentElement.insertBefore(row, first)
|
|
}
|
|
|
|
// Reacomodamos el orden
|
|
this.reorder()
|
|
|
|
// Mantenemos el primero a la vista
|
|
rows[0].row.scrollIntoView({ block: "center" });
|
|
}
|
|
|
|
bottom (event) {
|
|
event.preventDefault()
|
|
event.stopPropagation()
|
|
|
|
if (this.empty) return
|
|
|
|
const rows = this.sorted_rows
|
|
|
|
for (const r of rows) {
|
|
const row = r.row
|
|
|
|
row.parentElement.appendChild(row)
|
|
}
|
|
|
|
// Reacomodamos el orden
|
|
this.reorder()
|
|
|
|
// Mantenemos el primero a la vista
|
|
rows[0].row.scrollIntoView({ block: "center" });
|
|
}
|
|
|
|
/*
|
|
* Al cambiar los inputs, mantener la lista de filas actualizadas.
|
|
* Necesitamos saber la posición para poder mover las filas en orden
|
|
* en lugar del orden en que fueron seleccionadas.
|
|
*/
|
|
select (event) {
|
|
const row = event.target.closest('tr')
|
|
|
|
if (event.target.checked) {
|
|
this.selected_rows[row.id] = {
|
|
row,
|
|
order: this.rowTargets.indexOf(row)
|
|
}
|
|
} else {
|
|
delete this.selected_rows[row.id]
|
|
}
|
|
|
|
this.counter()
|
|
}
|
|
|
|
/*
|
|
* Mover hacia arriba
|
|
*/
|
|
up (event) {
|
|
event.preventDefault()
|
|
event.stopPropagation()
|
|
if (this.empty) return
|
|
|
|
this.move('up')
|
|
}
|
|
|
|
/*
|
|
* Mover hacia abajo
|
|
*/
|
|
down (event) {
|
|
event.preventDefault()
|
|
event.stopPropagation()
|
|
if (this.empty) return
|
|
|
|
this.move('down')
|
|
}
|
|
}
|