omniloop-ai / components /metric-card.js
00Boobs00's picture
**Project Title: The Infinite Loop of Intelligence – Building the Regenerative Self-Regenerator AI System**
411ed67 verified
class MetricCard extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
static get observedAttributes() {
return ['title', 'value', 'icon', 'color', 'trend', 'desc'];
}
connectedCallback() {
this.render();
// Listen for updates to simulate "live" data
document.addEventListener('update-metrics', () => {
if(Math.random() > 0.5) {
this.perturbValue();
}
});
}
attributeChangedCallback(name, oldValue, newValue) {
if (oldValue !== newValue) {
this.render();
}
}
perturbValue() {
const valueDisplay = this.shadowRoot.getElementById('metric-value');
const trendDisplay = this.shadowRoot.getElementById('metric-trend');
// Slight random fluctuation visual only
const original = valueDisplay.innerText;
valueDisplay.style.opacity = '0.7';
setTimeout(() => {
valueDisplay.style.opacity = '1';
// Sometimes change trend sign
if(Math.random() > 0.8) {
const isPos = trendDisplay.innerText.includes('+');
trendDisplay.innerText = isPos ? trendDisplay.innerText.replace('+', '-') : trendDisplay.innerText.replace('-', '+');
trendDisplay.className = isPos ? 'text-xs text-red-400' : 'text-xs text-ai-green';
}
}, 200);
}
render() {
const title = this.getAttribute('title') || 'Metric';
const value = this.getAttribute('value') || '0';
const icon = this.getAttribute('icon') || 'activity';
const color = this.getAttribute('color') || 'text-white';
const trend = this.getAttribute('trend') || '0%';
const desc = this.getAttribute('desc') || '';
this.shadowRoot.innerHTML = `
<style>
:host {
display: block;
}
.card {
background-color: #1e293b;
border: 1px solid #334155;
border-radius: 0.75rem;
padding: 1.5rem;
transition: transform 0.2s, box-shadow 0.2s;
height: 100%;
}
.card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 20px rgba(0,0,0,0.3);
border-color: #10b981;
}
.icon-box {
width: 40px;
height: 40px;
border-radius: 8px;
background-color: rgba(16, 185, 129, 0.1);
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 1rem;
}
</style>
<div class="card">
<div class="flex justify-between items-start">
<div>
<p class="text-slate-400 text-sm font-medium mb-1">${title}</p>
<h3 id="metric-value" class="text-3xl font-bold text-white transition-opacity duration-200">${value}</h3>
</div>
<div class="icon-box">
<i data-feather="${icon}" class="${color}"></i>
</div>
</div>
<div class="mt-4 flex items-center justify-between">
<span id="metric-trend" class="text-xs text-ai-green font-bold bg-green-900/30 px-2 py-1 rounded">
${trend}
</span>
<span class="text-xs text-slate-500">${desc}</span>
</div>
</div>
`;
// Initialize icon
if (window.feather) {
window.feather.replace();
}
}
}
customElements.define('metric-card', MetricCard);