From 044747a36b2b31bd239b005a01d8eb416635ff8e87436c8f65219df4c6e65e79 Mon Sep 17 00:00:00 2001 From: victor Date: Wed, 26 Aug 2026 15:45:25 -0300 Subject: [PATCH] =?UTF-8?q?Modifica=C3=A7=C3=A3o=20na=20area=20do=20motori?= =?UTF-8?q?sta?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/carrossel_controller.js | 80 +++++++++++-------- 1 file changed, 48 insertions(+), 32 deletions(-) diff --git a/app/javascript/controllers/carrossel_controller.js b/app/javascript/controllers/carrossel_controller.js index 2793498..a306cd7 100644 --- a/app/javascript/controllers/carrossel_controller.js +++ b/app/javascript/controllers/carrossel_controller.js @@ -1,27 +1,30 @@ // app/javascript/controllers/carrossel_controller.js -// Stimulus — paginação em "carrossel" para listas longas: mostra N itens por vez -// com setas grandes e o indicador "1–10 de 12". +// Stimulus — paginação em "carrossel" para listas longas: mostra uma página de +// itens por vez, com setas grandes e o indicador "1–5 de 12". // -// PADRÃO EM TELA PEQUENA, LISTA INTEIRA EM TELA GRANDE. Em celular e monitor -// pequeno, uma lista de 30 cards vira rolagem infinita e o rodapé/ações da tela -// somem de vista; num monitor grande a mesma lista cabe e paginar só atrapalha -// (Ctrl+F do navegador não acha o que está escondido). O corte é por largura de -// janela — `maxLargura` — e é reavaliado no resize/rotação da tela. +// TAMANHO DE PÁGINA POR TELA (é o ponto do controller): +// • celular (até 640px) → 5 itens — cada card ocupa muita altura; +// 10 já viravam rolagem infinita; +// • até 1366px → 10 itens — tablet e notebook pequeno; +// • acima disso → lista INTEIRA, sem setas. Em monitor grande a +// lista cabe e esconder item só atrapalha (o +// Ctrl+F do navegador não acha o que está oculto). +// +// O corte é reavaliado no resize/rotação: girar o celular ou encostar a janela +// na lateral já muda o comportamento, e o item que estava no topo continua na +// tela (a posição é guardada pelo ÍNDICE do item, não pelo número da página). // // Os itens já vêm todos no HTML; aqui só escondemos os de fora da página. Por // isso não serve para listas de centenas de itens (aí é paginação no servidor). // // Uso: //
<- acima disso, mostra tudo -//
<- cada item -// … -// +// data-carrossel-tamanho-value="10" <- itens por página +// data-carrossel-tamanho-compacto-value="5" <- itens no celular +// data-carrossel-largura-compacta-value="640" <- até aqui é "celular" +// data-carrossel-max-largura-value="1366"> <- acima disso, mostra tudo +//
+// <%= render 'shared/carrossel_controles' %> //
// // Um item com data-carrossel-inicial="true" (ex.: o motorista com a composição @@ -32,12 +35,17 @@ import { Controller } from "@hotwired/stimulus" export default class extends Controller { static targets = ["item", "controles", "indicador", "anterior", "proximo"] static values = { - tamanho: { type: Number, default: 10 }, - maxLargura: { type: Number, default: 1366 } + tamanho: { type: Number, default: 10 }, + tamanhoCompacto: { type: Number, default: 5 }, + larguraCompacta: { type: Number, default: 640 }, + maxLargura: { type: Number, default: 1366 } } connect() { - this.pagina = this.paginaInicial() + // Âncora = índice do primeiro item da página atual. Guardar o ÍNDICE (e não + // o número da página) é o que mantém o mesmo item na tela quando o tamanho + // da página muda junto com a largura. + this.ancora = this.indiceInicial() this._aoRedimensionar = () => this.render() window.addEventListener("resize", this._aoRedimensionar) this.render() @@ -51,26 +59,37 @@ export default class extends Controller { anterior() { this.ir(this.pagina - 1) } ir(destino) { - this.pagina = Math.min(Math.max(destino, 0), this.totalPaginas - 1) + const pagina = Math.min(Math.max(destino, 0), this.totalPaginas - 1) + this.ancora = pagina * this.tamanho this.render() - // Volta ao topo da lista: sem isso, na 2ª página o leitor continua vendo o - // fim da anterior e parece que nada mudou. + // Volta ao topo da lista: sem isso, na página seguinte o leitor continua + // vendo o fim da anterior e parece que nada mudou. this.element.scrollIntoView({ behavior: "smooth", block: "start" }) } + get tamanho() { + return window.innerWidth <= this.larguraCompactaValue + ? this.tamanhoCompactoValue + : this.tamanhoValue + } + // Tela grande: sem paginação (a lista inteira aparece). get paginando() { return window.innerWidth <= this.maxLarguraValue && - this.itemTargets.length > this.tamanhoValue + this.itemTargets.length > this.tamanho } get totalPaginas() { - return Math.max(Math.ceil(this.itemTargets.length / this.tamanhoValue), 1) + return Math.max(Math.ceil(this.itemTargets.length / this.tamanho), 1) } - paginaInicial() { + get pagina() { + return Math.min(Math.floor(this.ancora / this.tamanho), this.totalPaginas - 1) + } + + indiceInicial() { const i = this.itemTargets.findIndex((el) => el.dataset.carrosselInicial === "true") - return i < 0 ? 0 : Math.floor(i / this.tamanhoValue) + return i < 0 ? 0 : i } render() { @@ -82,11 +101,8 @@ export default class extends Controller { return } - // Uma tela menor pode ter reduzido o total de páginas desde a última vez. - this.pagina = Math.min(this.pagina, this.totalPaginas - 1) - - const inicio = this.pagina * this.tamanhoValue - const fim = inicio + this.tamanhoValue + const inicio = this.pagina * this.tamanho + const fim = inicio + this.tamanho this.itemTargets.forEach((item, i) => { item.classList.toggle("hidden", i < inicio || i >= fim) @@ -96,7 +112,7 @@ export default class extends Controller { this.controlesTarget.classList.remove("hidden") if (this.hasIndicadorTarget) { - // "1–10 de 12" e não "página 1 de 2": quem lê quer saber quantos itens + // "1–5 de 12" e não "página 1 de 3": quem lê quer saber quantos itens // existem, não quantas páginas a tela tem. this.indicadorTarget.textContent = `${inicio + 1}–${Math.min(fim, total)} de ${total}` }