feat(mobile): paginador CSS-only con flechas para activity filters
- activity_view.js: render adicional .actpager con N radios + cells (2 botones por celda) + arrow labels por página. Hidden inline en desktop (display:none). - mobile.css: .activity-filter-grid display:none, .actpager block. Reglas .actpager-rN:checked ~ .actpager-frame ... aplican translateX por porcentaje y muestran solo el set de flechas correspondiente. 18 páginas para 36 botones; reglas hasta página 30 por holgura. CSS puro: sin JS, sin scroll del dedo, sin caché. Las flechas < y > son labels que togglean radios escondidos via for=. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
75bb97b204
commit
c6acdbb692
2 changed files with 330 additions and 12 deletions
|
|
@ -399,15 +399,19 @@ textarea, input, select {
|
||||||
font-size: 16px !important;
|
font-size: 16px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- Filtros: wrap multilinea, todos los botones visibles ---- */
|
/* En móvil ocultamos el grid antiguo; usa el paginador (.actpager) */
|
||||||
|
.activity-filter-grid {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- Filtros (mode-buttons): wrap multilinea ---- */
|
||||||
.mode-buttons,
|
.mode-buttons,
|
||||||
.mode-buttons-cols,
|
.mode-buttons-cols,
|
||||||
.mode-buttons-row,
|
.mode-buttons-row,
|
||||||
.filter-group,
|
.filter-group,
|
||||||
.filters,
|
.filters,
|
||||||
.inhabitant-action,
|
.inhabitant-action,
|
||||||
.tribe-mode-buttons,
|
.tribe-mode-buttons {
|
||||||
.activity-filter-grid {
|
|
||||||
display: flex !important;
|
display: flex !important;
|
||||||
flex-direction: row !important;
|
flex-direction: row !important;
|
||||||
flex-wrap: wrap !important;
|
flex-wrap: wrap !important;
|
||||||
|
|
@ -416,25 +420,18 @@ textarea, input, select {
|
||||||
overflow: visible !important;
|
overflow: visible !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* En el activity-filter-grid las columnas (.activity-filter-col) se aplanan */
|
|
||||||
.activity-filter-grid .activity-filter-col {
|
|
||||||
display: contents !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mode-buttons form,
|
.mode-buttons form,
|
||||||
.mode-buttons-cols form,
|
.mode-buttons-cols form,
|
||||||
.mode-buttons-row form,
|
.mode-buttons-row form,
|
||||||
.filter-group form,
|
.filter-group form,
|
||||||
.filters form,
|
.filters form {
|
||||||
.activity-filter-grid form {
|
|
||||||
flex: 0 0 auto !important;
|
flex: 0 0 auto !important;
|
||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mode-buttons .filter-btn,
|
.mode-buttons .filter-btn,
|
||||||
.mode-buttons button,
|
.mode-buttons button,
|
||||||
.filter-group .filter-btn,
|
.filter-group .filter-btn {
|
||||||
.activity-filter-grid .filter-btn {
|
|
||||||
width: auto !important;
|
width: auto !important;
|
||||||
white-space: nowrap !important;
|
white-space: nowrap !important;
|
||||||
padding: 8px 14px !important;
|
padding: 8px 14px !important;
|
||||||
|
|
@ -732,3 +729,282 @@ pre, code {
|
||||||
overflow-x: hidden !important;
|
overflow-x: hidden !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
ACTPAGER — paginador móvil CSS-only para activity_view
|
||||||
|
============================================================ */
|
||||||
|
.actpager {
|
||||||
|
display: block !important;
|
||||||
|
width: 100% !important;
|
||||||
|
margin-top: 12px !important;
|
||||||
|
position: relative !important;
|
||||||
|
}
|
||||||
|
.actpager-radio {
|
||||||
|
position: absolute !important;
|
||||||
|
opacity: 0 !important;
|
||||||
|
pointer-events: none !important;
|
||||||
|
width: 0 !important;
|
||||||
|
height: 0 !important;
|
||||||
|
}
|
||||||
|
.actpager-frame {
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: column !important;
|
||||||
|
gap: 10px !important;
|
||||||
|
}
|
||||||
|
.actpager-clip {
|
||||||
|
overflow: hidden !important;
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
.actpager-row {
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: row !important;
|
||||||
|
flex-wrap: nowrap !important;
|
||||||
|
width: 100% !important;
|
||||||
|
transition: transform 0.3s ease !important;
|
||||||
|
transform: translateX(0) !important;
|
||||||
|
}
|
||||||
|
.actpager-cell {
|
||||||
|
flex: 0 0 100% !important;
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: row !important;
|
||||||
|
gap: 8px !important;
|
||||||
|
width: 100% !important;
|
||||||
|
box-sizing: border-box !important;
|
||||||
|
}
|
||||||
|
.actpager-form {
|
||||||
|
flex: 1 1 0 !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
min-width: 0 !important;
|
||||||
|
}
|
||||||
|
.actpager-cell .filter-btn {
|
||||||
|
width: 100% !important;
|
||||||
|
padding: 12px 10px !important;
|
||||||
|
font-size: 0.9rem !important;
|
||||||
|
font-weight: 700 !important;
|
||||||
|
white-space: nowrap !important;
|
||||||
|
overflow: hidden !important;
|
||||||
|
text-overflow: ellipsis !important;
|
||||||
|
border-radius: 8px !important;
|
||||||
|
}
|
||||||
|
.actpager-controls {
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: row !important;
|
||||||
|
justify-content: center !important;
|
||||||
|
align-items: center !important;
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
.actpager-arrows {
|
||||||
|
display: none !important;
|
||||||
|
flex-direction: row !important;
|
||||||
|
align-items: center !important;
|
||||||
|
justify-content: center !important;
|
||||||
|
gap: 18px !important;
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
.actpager-arrow {
|
||||||
|
width: 44px !important;
|
||||||
|
height: 44px !important;
|
||||||
|
display: flex !important;
|
||||||
|
align-items: center !important;
|
||||||
|
justify-content: center !important;
|
||||||
|
font-size: 1.7rem !important;
|
||||||
|
font-weight: 700 !important;
|
||||||
|
border-radius: 50% !important;
|
||||||
|
cursor: pointer !important;
|
||||||
|
user-select: none !important;
|
||||||
|
line-height: 1 !important;
|
||||||
|
}
|
||||||
|
.actpager-arrow.disabled {
|
||||||
|
opacity: 0.25 !important;
|
||||||
|
cursor: default !important;
|
||||||
|
pointer-events: none !important;
|
||||||
|
}
|
||||||
|
.actpager-counter {
|
||||||
|
font-size: 0.9rem !important;
|
||||||
|
font-weight: 700 !important;
|
||||||
|
min-width: 60px !important;
|
||||||
|
text-align: center !important;
|
||||||
|
letter-spacing: 0.05em !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reglas por página: traslación + arrows visibles */
|
||||||
|
.actpager-r0:checked ~ .actpager-frame .actpager-row {
|
||||||
|
transform: translateX(-0%) !important;
|
||||||
|
}
|
||||||
|
.actpager-r0:checked ~ .actpager-frame .actpager-a0 {
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
.actpager-r1:checked ~ .actpager-frame .actpager-row {
|
||||||
|
transform: translateX(-100%) !important;
|
||||||
|
}
|
||||||
|
.actpager-r1:checked ~ .actpager-frame .actpager-a1 {
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
.actpager-r2:checked ~ .actpager-frame .actpager-row {
|
||||||
|
transform: translateX(-200%) !important;
|
||||||
|
}
|
||||||
|
.actpager-r2:checked ~ .actpager-frame .actpager-a2 {
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
.actpager-r3:checked ~ .actpager-frame .actpager-row {
|
||||||
|
transform: translateX(-300%) !important;
|
||||||
|
}
|
||||||
|
.actpager-r3:checked ~ .actpager-frame .actpager-a3 {
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
.actpager-r4:checked ~ .actpager-frame .actpager-row {
|
||||||
|
transform: translateX(-400%) !important;
|
||||||
|
}
|
||||||
|
.actpager-r4:checked ~ .actpager-frame .actpager-a4 {
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
.actpager-r5:checked ~ .actpager-frame .actpager-row {
|
||||||
|
transform: translateX(-500%) !important;
|
||||||
|
}
|
||||||
|
.actpager-r5:checked ~ .actpager-frame .actpager-a5 {
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
.actpager-r6:checked ~ .actpager-frame .actpager-row {
|
||||||
|
transform: translateX(-600%) !important;
|
||||||
|
}
|
||||||
|
.actpager-r6:checked ~ .actpager-frame .actpager-a6 {
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
.actpager-r7:checked ~ .actpager-frame .actpager-row {
|
||||||
|
transform: translateX(-700%) !important;
|
||||||
|
}
|
||||||
|
.actpager-r7:checked ~ .actpager-frame .actpager-a7 {
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
.actpager-r8:checked ~ .actpager-frame .actpager-row {
|
||||||
|
transform: translateX(-800%) !important;
|
||||||
|
}
|
||||||
|
.actpager-r8:checked ~ .actpager-frame .actpager-a8 {
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
.actpager-r9:checked ~ .actpager-frame .actpager-row {
|
||||||
|
transform: translateX(-900%) !important;
|
||||||
|
}
|
||||||
|
.actpager-r9:checked ~ .actpager-frame .actpager-a9 {
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
.actpager-r10:checked ~ .actpager-frame .actpager-row {
|
||||||
|
transform: translateX(-1000%) !important;
|
||||||
|
}
|
||||||
|
.actpager-r10:checked ~ .actpager-frame .actpager-a10 {
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
.actpager-r11:checked ~ .actpager-frame .actpager-row {
|
||||||
|
transform: translateX(-1100%) !important;
|
||||||
|
}
|
||||||
|
.actpager-r11:checked ~ .actpager-frame .actpager-a11 {
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
.actpager-r12:checked ~ .actpager-frame .actpager-row {
|
||||||
|
transform: translateX(-1200%) !important;
|
||||||
|
}
|
||||||
|
.actpager-r12:checked ~ .actpager-frame .actpager-a12 {
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
.actpager-r13:checked ~ .actpager-frame .actpager-row {
|
||||||
|
transform: translateX(-1300%) !important;
|
||||||
|
}
|
||||||
|
.actpager-r13:checked ~ .actpager-frame .actpager-a13 {
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
.actpager-r14:checked ~ .actpager-frame .actpager-row {
|
||||||
|
transform: translateX(-1400%) !important;
|
||||||
|
}
|
||||||
|
.actpager-r14:checked ~ .actpager-frame .actpager-a14 {
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
.actpager-r15:checked ~ .actpager-frame .actpager-row {
|
||||||
|
transform: translateX(-1500%) !important;
|
||||||
|
}
|
||||||
|
.actpager-r15:checked ~ .actpager-frame .actpager-a15 {
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
.actpager-r16:checked ~ .actpager-frame .actpager-row {
|
||||||
|
transform: translateX(-1600%) !important;
|
||||||
|
}
|
||||||
|
.actpager-r16:checked ~ .actpager-frame .actpager-a16 {
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
.actpager-r17:checked ~ .actpager-frame .actpager-row {
|
||||||
|
transform: translateX(-1700%) !important;
|
||||||
|
}
|
||||||
|
.actpager-r17:checked ~ .actpager-frame .actpager-a17 {
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
.actpager-r18:checked ~ .actpager-frame .actpager-row {
|
||||||
|
transform: translateX(-1800%) !important;
|
||||||
|
}
|
||||||
|
.actpager-r18:checked ~ .actpager-frame .actpager-a18 {
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
.actpager-r19:checked ~ .actpager-frame .actpager-row {
|
||||||
|
transform: translateX(-1900%) !important;
|
||||||
|
}
|
||||||
|
.actpager-r19:checked ~ .actpager-frame .actpager-a19 {
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
.actpager-r20:checked ~ .actpager-frame .actpager-row {
|
||||||
|
transform: translateX(-2000%) !important;
|
||||||
|
}
|
||||||
|
.actpager-r20:checked ~ .actpager-frame .actpager-a20 {
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
.actpager-r21:checked ~ .actpager-frame .actpager-row {
|
||||||
|
transform: translateX(-2100%) !important;
|
||||||
|
}
|
||||||
|
.actpager-r21:checked ~ .actpager-frame .actpager-a21 {
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
.actpager-r22:checked ~ .actpager-frame .actpager-row {
|
||||||
|
transform: translateX(-2200%) !important;
|
||||||
|
}
|
||||||
|
.actpager-r22:checked ~ .actpager-frame .actpager-a22 {
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
.actpager-r23:checked ~ .actpager-frame .actpager-row {
|
||||||
|
transform: translateX(-2300%) !important;
|
||||||
|
}
|
||||||
|
.actpager-r23:checked ~ .actpager-frame .actpager-a23 {
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
.actpager-r24:checked ~ .actpager-frame .actpager-row {
|
||||||
|
transform: translateX(-2400%) !important;
|
||||||
|
}
|
||||||
|
.actpager-r24:checked ~ .actpager-frame .actpager-a24 {
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
.actpager-r25:checked ~ .actpager-frame .actpager-row {
|
||||||
|
transform: translateX(-2500%) !important;
|
||||||
|
}
|
||||||
|
.actpager-r25:checked ~ .actpager-frame .actpager-a25 {
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
.actpager-r26:checked ~ .actpager-frame .actpager-row {
|
||||||
|
transform: translateX(-2600%) !important;
|
||||||
|
}
|
||||||
|
.actpager-r26:checked ~ .actpager-frame .actpager-a26 {
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
.actpager-r27:checked ~ .actpager-frame .actpager-row {
|
||||||
|
transform: translateX(-2700%) !important;
|
||||||
|
}
|
||||||
|
.actpager-r27:checked ~ .actpager-frame .actpager-a27 {
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
.actpager-r28:checked ~ .actpager-frame .actpager-row {
|
||||||
|
transform: translateX(-2800%) !important;
|
||||||
|
}
|
||||||
|
.actpager-r28:checked ~ .actpager-frame .actpager-a28 {
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
.actpager-r29:checked ~ .actpager-frame .actpager-row {
|
||||||
|
transform: translateX(-2900%) !important;
|
||||||
|
}
|
||||||
|
.actpager-r29:checked ~ .actpager-frame .actpager-a29 {
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1701,6 +1701,47 @@ exports.activityView = (actions, filter, userId, q = '') => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mobile paginator (CSS-only, no JS): 2 buttons per page with arrow labels
|
||||||
|
const buildBtn = (cls, t, lab) => form({ method: 'GET', action: '/activity', class: cls },
|
||||||
|
input({ type: 'hidden', name: 'filter', value: t }),
|
||||||
|
button({ type: 'submit', class: filter === t ? 'filter-btn active' : 'filter-btn' }, lab)
|
||||||
|
);
|
||||||
|
const PAGE_SIZE = 2;
|
||||||
|
const totalPages = Math.ceil(activityTypes.length / PAGE_SIZE);
|
||||||
|
const pageCells = [];
|
||||||
|
for (let i = 0; i < totalPages; i++) {
|
||||||
|
const slice = activityTypes.slice(i * PAGE_SIZE, (i + 1) * PAGE_SIZE);
|
||||||
|
pageCells.push(div({ class: 'actpager-cell' },
|
||||||
|
slice.map(({ type, label }) => buildBtn('actpager-form', type, label))
|
||||||
|
));
|
||||||
|
}
|
||||||
|
const radios = [];
|
||||||
|
for (let i = 0; i < totalPages; i++) {
|
||||||
|
const attrs = { type: 'radio', name: 'actp', id: `actp${i}`, class: `actpager-radio actpager-r${i}` };
|
||||||
|
if (i === 0) attrs.checked = 'checked';
|
||||||
|
radios.push(input(attrs));
|
||||||
|
}
|
||||||
|
const arrowSets = [];
|
||||||
|
for (let i = 0; i < totalPages; i++) {
|
||||||
|
const prev = i > 0
|
||||||
|
? require('../server/node_modules/hyperaxe').label({ for: `actp${i - 1}`, class: 'actpager-arrow' }, '‹')
|
||||||
|
: require('../server/node_modules/hyperaxe').span({ class: 'actpager-arrow disabled' }, '‹');
|
||||||
|
const next = i < totalPages - 1
|
||||||
|
? require('../server/node_modules/hyperaxe').label({ for: `actp${i + 1}`, class: 'actpager-arrow' }, '›')
|
||||||
|
: require('../server/node_modules/hyperaxe').span({ class: 'actpager-arrow disabled' }, '›');
|
||||||
|
const counter = require('../server/node_modules/hyperaxe').span({ class: 'actpager-counter' }, `${i + 1} / ${totalPages}`);
|
||||||
|
arrowSets.push(div({ class: `actpager-arrows actpager-a${i}` }, prev, counter, next));
|
||||||
|
}
|
||||||
|
const mobilePager = section({ class: 'actpager', style: 'display:none' },
|
||||||
|
...radios,
|
||||||
|
div({ class: 'actpager-frame' },
|
||||||
|
div({ class: 'actpager-clip' },
|
||||||
|
div({ class: 'actpager-row' }, ...pageCells)
|
||||||
|
),
|
||||||
|
div({ class: 'actpager-controls' }, ...arrowSets)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
let html = template(
|
let html = template(
|
||||||
title,
|
title,
|
||||||
section(
|
section(
|
||||||
|
|
@ -1708,6 +1749,7 @@ exports.activityView = (actions, filter, userId, q = '') => {
|
||||||
h2(i18n.activityList),
|
h2(i18n.activityList),
|
||||||
p(desc)
|
p(desc)
|
||||||
),
|
),
|
||||||
|
mobilePager,
|
||||||
div({ class: 'activity-filter-grid' },
|
div({ class: 'activity-filter-grid' },
|
||||||
...[
|
...[
|
||||||
activityTypes.slice(0, 4),
|
activityTypes.slice(0, 4),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue