test_445 / trackio /ui /components /alert_panel.py
abidlabs's picture
abidlabs HF Staff
Upload folder using huggingface_hub
0561478 verified
import gradio as gr
HTML_TEMPLATE = """\
<div class="alert-header">
<span class="alert-count">Alerts</span>
<span class="alert-badge-count">${value.length}</span>
<span class="alert-chevron">▾</span>
</div>
<div class="alert-body">
<div class="alert-filters">
<button class="filter-pill filter-active" data-level="info">🔵 Info</button>
<button class="filter-pill filter-active" data-level="warn">🟡 Warn</button>
<button class="filter-pill filter-active" data-level="error">🔴 Error</button>
</div>
{{#each value}}
<div class="alert-item alert-{{this.level}}" data-level="{{this.level}}">
<div class="alert-item-top">
<span class="alert-badge">{{this.badge}}</span>
<span class="alert-title">{{this.title}}</span>
</div>
<div class="alert-item-meta">{{this.meta}}</div>
{{#if this.text}}<div class="alert-item-text">{{this.text}}</div>{{/if}}
</div>
{{/each}}
</div>
"""
CSS_TEMPLATE = """\
position: fixed;
bottom: 24px;
right: 24px;
width: 33%;
min-width: 320px;
max-width: 480px;
z-index: 9999;
border-radius: 12px;
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.12);
background: var(--background-fill-primary, #fff);
border: 1px solid var(--border-color-primary, #e5e7eb);
font-family: var(--font, system-ui, sans-serif);
font-size: 14px;
color: var(--body-text-color, #374151);
overflow: hidden;
display: none;
.alert-header {
display: flex;
align-items: center;
gap: 8px;
padding: 10px 16px;
cursor: pointer;
user-select: none;
background: var(--background-fill-secondary, #f9fafb);
}
.alert-count { flex: 1; font-weight: 600; font-size: 13px; }
.alert-badge-count {
background: var(--body-text-color-subdued, #6b7280);
color: #fff;
font-size: 11px;
font-weight: 700;
min-width: 20px;
height: 20px;
line-height: 20px;
text-align: center;
border-radius: 10px;
padding: 0 6px;
}
.alert-chevron {
font-size: 12px;
color: var(--body-text-color-subdued, #6b7280);
transition: transform 0.2s;
}
.alert-body {
max-height: 0;
overflow: hidden;
}
.alert-filters {
display: flex;
gap: 6px;
padding: 8px 16px;
border-bottom: 1px solid var(--border-color-primary, #e5e7eb);
background: var(--background-fill-primary, #fff);
position: sticky;
top: 0;
z-index: 1;
}
.filter-pill {
font-size: 11px;
padding: 3px 10px;
border-radius: 12px;
border: 1px solid var(--border-color-primary, #e5e7eb);
background: var(--background-fill-primary, #fff);
color: var(--body-text-color-subdued, #6b7280);
cursor: pointer;
opacity: 0.45;
transition: opacity 0.15s;
}
.filter-pill.filter-active {
opacity: 1;
font-weight: 600;
color: var(--body-text-color, #374151);
}
.alert-item {
padding: 10px 16px;
border-bottom: 1px solid var(--border-color-primary, #e5e7eb);
border-left: 3px solid transparent;
}
.alert-item:last-child { border-bottom: none; }
.alert-item.hidden { display: none; }
.alert-info { border-left-color: #3b82f6; }
.alert-warn { border-left-color: #f59e0b; }
.alert-error { border-left-color: #ef4444; }
.alert-item-top {
display: flex;
align-items: center;
gap: 6px;
}
.alert-badge { font-size: 10px; }
.alert-title { font-weight: 600; font-size: 13px; }
.alert-item-meta {
font-size: 11px;
color: var(--body-text-color-subdued, #6b7280);
margin-top: 2px;
}
.alert-item-text {
margin-top: 4px;
font-size: 12px;
color: var(--body-text-color-subdued, #6b7280);
line-height: 1.4;
}
@keyframes alert-header-flash {
0%, 100% { background: var(--background-fill-secondary, #f9fafb); }
50% { background: #fef3c7; }
}
.alert-header.flash {
animation: alert-header-flash 0.5s ease-in-out 3;
}
"""
JS_ON_LOAD = """\
const _stored = sessionStorage.getItem('trackio_alert_expanded');
let isExpanded = _stored === null ? true : _stored === 'true';
let alertCount = 0;
const activeFilters = new Set(['info', 'warn', 'error']);
function applyFilters() {
element.querySelectorAll('.alert-item').forEach(item => {
const level = item.getAttribute('data-level');
item.classList.toggle('hidden', !activeFilters.has(level));
});
}
function restoreState(animate) {
const body = element.querySelector('.alert-body');
const chevron = element.querySelector('.alert-chevron');
if (!body || !chevron) return;
if (!animate) {
body.style.transition = 'none';
void body.offsetHeight;
} else {
body.style.transition = 'max-height 0.3s ease';
}
body.style.maxHeight = isExpanded ? '50vh' : '0';
body.style.overflow = isExpanded ? 'auto' : 'hidden';
chevron.style.transform = isExpanded ? 'rotate(180deg)' : '';
if (!animate) {
void body.offsetHeight;
body.style.transition = 'max-height 0.3s ease';
}
applyFilters();
}
element.addEventListener('click', (e) => {
const pill = e.target.closest('.filter-pill');
if (pill) {
const level = pill.getAttribute('data-level');
if (activeFilters.has(level)) {
activeFilters.delete(level);
pill.classList.remove('filter-active');
} else {
activeFilters.add(level);
pill.classList.add('filter-active');
}
applyFilters();
return;
}
if (e.target.closest('.alert-header')) {
isExpanded = !isExpanded;
sessionStorage.setItem('trackio_alert_expanded', isExpanded);
restoreState(true);
if (isExpanded) {
setTimeout(() => {
const body = element.querySelector('.alert-body');
if (body) body.scrollTop = body.scrollHeight;
}, 320);
}
}
});
const observer = new MutationObserver(() => {
requestAnimationFrame(() => {
const items = element.querySelectorAll('.alert-item');
const newCount = items.length;
element.style.display = newCount === 0 ? 'none' : 'block';
restoreState(false);
const pills = element.querySelectorAll('.filter-pill');
pills.forEach(p => {
p.classList.toggle('filter-active',
activeFilters.has(p.getAttribute('data-level')));
});
if (newCount > alertCount && alertCount > 0) {
const header = element.querySelector('.alert-header');
if (header) {
header.classList.remove('flash');
void header.offsetWidth;
header.classList.add('flash');
}
if (isExpanded) {
const body = element.querySelector('.alert-body');
if (body) body.scrollTop = body.scrollHeight;
}
}
alertCount = newCount;
});
});
observer.observe(element, { childList: true, subtree: true });
"""
class AlertPanel(gr.HTML):
def __init__(self, **kwargs):
super().__init__(
value=[],
html_template=HTML_TEMPLATE,
css_template=CSS_TEMPLATE,
js_on_load=JS_ON_LOAD,
**kwargs,
)