feat(web): cautare + paginare client-side pe tabelele din Mapari
Maparile pot creste la sute de randuri. Enhancer reutilizabil (data-dt) in base.html filtreaza si pagineaza DOM-ul deja randat, fara cereri server; re-init la full load si dupa swap-urile HTMX. Aplicat pe cele 3 tabele (De rezolvat / operatii salvate / formate coloane). Randurile cu <select> expun haystack explicit prin data-dt-row (cod_op + cod_rar + denumire): altfel optiunile selectului ar pune tot nomenclatorul in textContent si orice cautare ar potrivi orice rand. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -150,6 +150,18 @@
|
||||
border-radius:6px; cursor:pointer; min-height:36px; white-space:nowrap; }
|
||||
.kebab-menu button:hover, .kebab-menu a:hover { background:var(--line); }
|
||||
.kebab-menu button.danger { color:var(--err); }
|
||||
/* Tabel cu cautare + paginare client-side (data-dt). Maparile pot creste la sute de randuri;
|
||||
filtram/paginez DOM-ul deja randat, fara cereri suplimentare. Vezi scriptul din base.html. */
|
||||
input[type=search] { font:inherit; background:var(--bg); color:var(--ink); border:1px solid var(--line);
|
||||
border-radius:6px; padding:6px 10px; width:100%; }
|
||||
.dt-tools { display:flex; align-items:center; gap:8px; margin:0 0 10px; }
|
||||
.dt-search { flex:1 1 auto; max-width:320px; }
|
||||
.dt-empty { color:var(--muted); padding:16px; text-align:center; font-size:13px; }
|
||||
.dt-pager { display:flex; align-items:center; justify-content:flex-end; gap:10px;
|
||||
margin-top:10px; font-size:13px; color:var(--muted); }
|
||||
.dt-pager button { background:transparent; color:var(--ink); border:1px solid var(--line);
|
||||
padding:5px 12px; min-height:32px; }
|
||||
.dt-pager button:disabled { opacity:.45; cursor:default; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -295,5 +307,68 @@
|
||||
window.addEventListener('resize', function() { closeAll(null); });
|
||||
})();
|
||||
</script>
|
||||
<script>
|
||||
// Cautare + paginare client-side pentru tabele mari (data-dt="<page_size>"). Filtreaza si
|
||||
// pagineaza DOM-ul deja randat (fara cereri server) — potrivit pentru maparile care pot creste
|
||||
// la sute de randuri. Re-init la full load SI dupa swap-urile HTMX (tab Mapari, salvare/stergere).
|
||||
// Markup: <div data-dt="10"> [input data-dt-search] <table> [div data-dt-empty] [div data-dt-pager] </div>
|
||||
(function() {
|
||||
function enhance(scope) {
|
||||
(scope || document).querySelectorAll('[data-dt]').forEach(function(wrap) {
|
||||
if (wrap.__dt) return; // idempotent (afterSettle poate re-scana)
|
||||
wrap.__dt = true;
|
||||
var table = wrap.querySelector('table');
|
||||
var tbody = table && table.tBodies[0];
|
||||
if (!tbody) return;
|
||||
var size = parseInt(wrap.getAttribute('data-dt'), 10) || 10;
|
||||
var search = wrap.querySelector('[data-dt-search]');
|
||||
var pager = wrap.querySelector('[data-dt-pager]');
|
||||
var empty = wrap.querySelector('[data-dt-empty]');
|
||||
var rows = Array.prototype.slice.call(tbody.rows);
|
||||
var page = 1;
|
||||
// Haystack-ul randului: atributul data-dt-row daca exista, altfel textContent. Necesar
|
||||
// cand randul contine un <select> (optiunile lui ar pune tot nomenclatorul in textContent
|
||||
// -> orice cautare ar potrivi orice rand). Vezi data-dt-row in _mapari.html.
|
||||
function haystack(r) {
|
||||
var h = r.getAttribute('data-dt-row');
|
||||
return (h !== null ? h : r.textContent).toLowerCase();
|
||||
}
|
||||
function matched() {
|
||||
var q = (search && search.value || '').trim().toLowerCase();
|
||||
if (!q) return rows;
|
||||
return rows.filter(function(r) { return haystack(r).indexOf(q) !== -1; });
|
||||
}
|
||||
function draw() {
|
||||
var fr = matched();
|
||||
var pages = Math.max(1, Math.ceil(fr.length / size));
|
||||
if (page > pages) page = pages;
|
||||
if (page < 1) page = 1;
|
||||
var start = (page - 1) * size;
|
||||
rows.forEach(function(r) { r.style.display = 'none'; });
|
||||
fr.slice(start, start + size).forEach(function(r) { r.style.display = ''; });
|
||||
if (empty) empty.style.display = fr.length ? 'none' : '';
|
||||
if (!pager) return;
|
||||
if (fr.length <= size && page === 1) { pager.style.display = 'none'; pager.innerHTML = ''; return; }
|
||||
pager.style.display = '';
|
||||
pager.innerHTML = '';
|
||||
var info = document.createElement('span');
|
||||
info.textContent = (fr.length ? start + 1 : 0) + '–' +
|
||||
Math.min(start + size, fr.length) + ' din ' + fr.length;
|
||||
var prev = document.createElement('button');
|
||||
prev.type = 'button'; prev.textContent = 'Inapoi'; prev.disabled = page <= 1;
|
||||
prev.addEventListener('click', function() { page--; draw(); });
|
||||
var next = document.createElement('button');
|
||||
next.type = 'button'; next.textContent = 'Inainte'; next.disabled = page >= pages;
|
||||
next.addEventListener('click', function() { page++; draw(); });
|
||||
pager.appendChild(info); pager.appendChild(prev); pager.appendChild(next);
|
||||
}
|
||||
if (search) search.addEventListener('input', function() { page = 1; draw(); });
|
||||
draw();
|
||||
});
|
||||
}
|
||||
document.addEventListener('DOMContentLoaded', function() { enhance(document); });
|
||||
document.body.addEventListener('htmx:afterSettle', function(e) { enhance(e.target); });
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user