infostream-nexus / components /topic-filter.js
TheContrast's picture
So, is this just the code to put somewhere, and that in turn would give me the app?
7f2753f verified
class TopicFilter extends HTMLElement {
constructor() {
super();
}
connectedCallback() {
this.attachShadow({ mode: 'open' });
this.render();
this.setupEvents();
}
render() {
const dark = document.documentElement.classList.contains('dark');
this.shadowRoot.innerHTML = `
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:host {
display: block;
}
.filter-container {
display: flex;
align-items: center;
gap: 0.5rem;
overflow-x: auto;
padding: 0.25rem;
scrollbar-width: none;
-ms-overflow-style: none;
}
.filter-container::-webkit-scrollbar {
display: none;
}
.filter-btn {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 1rem;
border-radius: 9999px;
border: 1px solid #e5e7eb;
background: white;
color: #374151;
font-size: 0.875rem;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
white-space: nowrap;
flex-shrink: 0;
}
:host-context(.dark) .filter-btn {
background: #1f2937;
border-color: #374151;
color: #d1d5db;
}
.filter-btn:hover {
border-color: #0ea5e9;
color: #0ea5e9;
}
.filter-btn.active {
background: linear-gradient(135deg, #0ea5e9, #8b5cf6);
border-color: transparent;
color: white;
transform: scale(1.05);
}
.filter-btn svg {
width: 16px;
height: 16px;
}
.filter-label {
font-size: 0.875rem;
font-weight: 600;
color: #6b7280;
margin-right: 0.5rem;
flex-shrink: 0;
}
:host-context(.dark) .filter-label {
color: #9ca3af;
}
</style>
<div class="filter-container">
<span class="filter-label">Filter:</span>
<button class="filter-btn active" data-filter="all">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7"></rect><rect x="14" y="3" width="7" height="7"></rect><rect x="14" y="14" width="7" height="7"></rect><rect x="3" y="14" width="7" height="7"></rect></svg>
All Topics
</button>
<button class="filter-btn" data-filter="ai">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z"></path><path d="m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z"></path></svg>
AI
</button>
<button class="filter-btn" data-filter="science">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="2" y1="12" x2="22" y2="12"></line><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"></path></svg>
Science
</button>
<button class="filter-btn" data-filter="tech">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="4" width="16" height="16" rx="2" ry="2"></rect><rect x="9" y="9" width="6" height="6"></rect><line x1="9" y1="1" x2="9" y2="4"></line><line x1="15" y1="1" x2="15" y2="4"></line><line x1="9" y1="20" x2="9" y2="23"></line><line x1="15" y1="20" x2="15" y2="23"></line><line x1="20" y1="9" x2="23" y2="9"></line><line x1="20" y1="14" x2="23" y2="14"></line><line x1="1" y1="9" x2="4" y2="9"></line><line x1="1" y1="14" x2="4" y2="14"></line></svg>
Tech
</button>
<button class="filter-btn" data-filter="nhl" style="color: #f47920;">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path></svg>
NHL
</button>
</div>
`;
}
setupEvents() {
const buttons = this.shadowRoot.querySelectorAll('.filter-btn');
buttons.forEach(btn => {
btn.addEventListener('click', () => {
buttons.forEach(b => b.classList.remove('active'));
btn.classList.add('active');
const filter = btn.dataset.filter;
window.dispatchEvent(new CustomEvent('filterchange', { detail: { filter } }));
});
});
}
}
customElements.define('topic-filter', TopicFilter);