Quellenübersicht: Klick auf Quelle klappt Artikel-Liste auf
Quellen-Boxen waren bisher reine Anzeige. Jetzt sind sie klickbar: beim Klick erscheint direkt unter der Box (über die volle Grid-Breite) eine Liste der Artikel-Headlines dieser Quelle, jede mit Link zum Originalartikel. Mutual-exclusive — Klick auf eine andere Quelle schließt die vorherige automatisch. - components.js: Item bekommt data-source, onclick + Tastatur-Support (Enter/Space), aria-expanded. - app.js: toggleSourceOverviewDetail filtert _currentArticles nach Quelle, sortiert chronologisch absteigend, fügt das Detail-Element via insertAdjacentElement direkt nach dem geklickten Item ein. - CSS: aktiver Item-Status (Glow + Tint), Detail-Block mit grid-column 1/-1 (volle Breite) + max-height 320px scrollbar bei vielen Artikeln + dezente Slide-In-Animation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Dieser Commit ist enthalten in:
@@ -866,6 +866,54 @@ const App = {
|
||||
}
|
||||
},
|
||||
|
||||
/** Klick auf eine Quellen-Box: Liste der Artikel inline aufklappen (mutual-exclusive). */
|
||||
toggleSourceOverviewDetail(el) {
|
||||
if (!el) return;
|
||||
const grid = el.parentElement;
|
||||
if (!grid) return;
|
||||
const sourceName = el.dataset.source || '';
|
||||
const wasActive = el.classList.contains('active');
|
||||
|
||||
// Alle anderen schliessen + bestehendes Detail entfernen
|
||||
grid.querySelectorAll('.source-overview-item.active').forEach(it => {
|
||||
it.classList.remove('active');
|
||||
it.setAttribute('aria-expanded', 'false');
|
||||
});
|
||||
const existingDetail = grid.querySelector('.source-overview-detail');
|
||||
if (existingDetail) existingDetail.remove();
|
||||
|
||||
// Wenn das geklickte Item bereits aktiv war: nur schliessen
|
||||
if (wasActive) return;
|
||||
|
||||
// Neues Detail einfuegen direkt nach dem geklickten Item
|
||||
el.classList.add('active');
|
||||
el.setAttribute('aria-expanded', 'true');
|
||||
|
||||
const articles = (this._currentArticles || [])
|
||||
.filter(a => (a.source || 'Unbekannt') === sourceName)
|
||||
.sort((a, b) => {
|
||||
const ta = new Date(a.collected_at || a.published_at || 0).getTime();
|
||||
const tb = new Date(b.collected_at || b.published_at || 0).getTime();
|
||||
return tb - ta;
|
||||
});
|
||||
|
||||
const detail = document.createElement('div');
|
||||
detail.className = 'source-overview-detail';
|
||||
if (articles.length === 0) {
|
||||
detail.innerHTML = '<div class="source-overview-detail-empty">Keine Artikel gefunden.</div>';
|
||||
} else {
|
||||
const items = articles.map(a => {
|
||||
const headline = UI.escape(a.headline_de || a.headline || '(ohne Titel)');
|
||||
if (a.source_url) {
|
||||
return `<li><a href="${UI.escape(a.source_url)}" target="_blank" rel="noopener">${headline}</a></li>`;
|
||||
}
|
||||
return `<li>${headline}</li>`;
|
||||
}).join('');
|
||||
detail.innerHTML = `<ul class="source-overview-detail-list">${items}</ul>`;
|
||||
}
|
||||
el.insertAdjacentElement('afterend', detail);
|
||||
},
|
||||
|
||||
/** Restliche Artikel seitenweise im Hintergrund nachladen und in _currentArticles mergen. */
|
||||
async _loadRemainingArticlesInBackground(incidentId) {
|
||||
const BATCH = 500;
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren