v0.11.4
This commit is contained in:
+64
-8
@@ -134,22 +134,71 @@ function timeLabel(ts, hours) {
|
||||
return new Date(ts).toLocaleString(locale(), options);
|
||||
}
|
||||
|
||||
function chartCardIsFullscreen(card) {
|
||||
return !!card && card.classList.contains('chart-fullscreen-fallback');
|
||||
}
|
||||
|
||||
function updateChartFullscreenButton(card) {
|
||||
if (!card) return;
|
||||
const button = card.querySelector('[data-chart-fullscreen]');
|
||||
if (!button) return;
|
||||
const active = chartCardIsFullscreen(card);
|
||||
button.textContent = active ? '×' : '⛶';
|
||||
button.title = tr(active ? 'history.exitFullscreen' : 'history.fullscreen');
|
||||
button.setAttribute('aria-label', button.title);
|
||||
}
|
||||
|
||||
function closeChartPreview(card) {
|
||||
if (!card) return;
|
||||
card.classList.remove('chart-fullscreen-fallback');
|
||||
document.body.classList.remove('chart-fullscreen-open');
|
||||
updateChartFullscreenButton(card);
|
||||
const canvas = card.querySelector('canvas[id]');
|
||||
if (canvas?.id) requestAnimationFrame(() => redrawHistoryChart(canvas.id));
|
||||
}
|
||||
|
||||
function toggleChartFullscreen(id) {
|
||||
const canvas = document.getElementById(id);
|
||||
const card = canvas?.closest('.history-chart-card');
|
||||
if (!card) return;
|
||||
|
||||
const active = chartCardIsFullscreen(card);
|
||||
const opened = document.querySelector('.history-chart-card.chart-fullscreen-fallback');
|
||||
if (opened && opened !== card) closeChartPreview(opened);
|
||||
|
||||
if (active) {
|
||||
closeChartPreview(card);
|
||||
return;
|
||||
}
|
||||
|
||||
card.classList.add('chart-fullscreen-fallback');
|
||||
document.body.classList.add('chart-fullscreen-open');
|
||||
updateChartFullscreenButton(card);
|
||||
requestAnimationFrame(() => redrawHistoryChart(id));
|
||||
}
|
||||
|
||||
function prepareCanvas(canvas, height) {
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
const wrapWidth = Math.floor(canvas.parentElement?.clientWidth || rect.width || 720);
|
||||
const wrap = canvas.parentElement;
|
||||
const wrapWidth = Math.floor(wrap?.clientWidth || rect.width || 720);
|
||||
const card = canvas.closest('.history-chart-card');
|
||||
const fullscreenHeight = chartCardIsFullscreen(card) ? Math.floor(wrap?.clientHeight || 0) : 0;
|
||||
const actualHeight = Math.max(height, fullscreenHeight || 0);
|
||||
const zoom = clamp(Number(canvas.dataset.chartZoom || 1), 1, 4);
|
||||
const width = Math.max(720, Math.floor(Math.max(720, wrapWidth) * zoom));
|
||||
const dpr = window.devicePixelRatio || 1;
|
||||
canvas.width = width * dpr; canvas.height = height * dpr;
|
||||
canvas.style.width = `${width}px`; canvas.style.height = `${height}px`;
|
||||
canvas.width = width * dpr; canvas.height = actualHeight * dpr;
|
||||
canvas.style.width = `${width}px`; canvas.style.height = `${actualHeight}px`;
|
||||
const ctx = canvas.getContext('2d'); ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
||||
ctx.clearRect(0, 0, width, height);
|
||||
return { ctx, width, height };
|
||||
ctx.clearRect(0, 0, width, actualHeight);
|
||||
return { ctx, width, height:actualHeight };
|
||||
}
|
||||
|
||||
function drawEmptyChart(canvas, height = 340) {
|
||||
if (!canvas) return;
|
||||
const { ctx, width } = prepareCanvas(canvas, height);
|
||||
const prepared = prepareCanvas(canvas, height);
|
||||
const { ctx, width } = prepared;
|
||||
height = prepared.height;
|
||||
ctx.fillStyle = cssColor('--muted', '#888'); ctx.font = '13px system-ui'; ctx.textAlign = 'center';
|
||||
ctx.fillText(tr('history.noData'), width / 2, height / 2);
|
||||
const wrap = canvas.parentElement;
|
||||
@@ -392,7 +441,9 @@ function drawLineChart(canvas, series, rows, { height = 340, minValue = null, ma
|
||||
const visibleSeries = series.filter((item, index) => !isChartSeriesHidden(canvas.id, item, index));
|
||||
if (!rows.length || !visibleSeries.length) return drawEmptyChart(canvas, height);
|
||||
const sortedRows = [...rows].sort((a, b) => new Date(a.timestamp) - new Date(b.timestamp));
|
||||
const { ctx, width } = prepareCanvas(canvas, height);
|
||||
const prepared = prepareCanvas(canvas, height);
|
||||
const { ctx, width } = prepared;
|
||||
height = prepared.height;
|
||||
const text = cssColor('--muted', '#888'), grid = cssColor('--grid', '#333');
|
||||
const pad = { left: 54, right: 20, top: 20, bottom: 42 };
|
||||
const allValues = [];
|
||||
@@ -445,10 +496,15 @@ function renderLegend(host, series) {
|
||||
function historyChartMarkup(id, title, hint, compact = false) {
|
||||
const zoom = clamp(Number(app.chartZooms[id] || 1), 1, MAX_CHART_ZOOM);
|
||||
const percent = Math.round(zoom * 100);
|
||||
return `<div class="panel chart-panel history-chart-card"><div class="chart-title-row"><div><h3>${esc(title)}</h3><p>${esc(hint || '')}</p><small class="chart-hover-hint">${esc(tr('history.hoverHint'))}</small></div><div class="chart-zoom-controls" aria-label="${esc(tr('history.zoomControls'))}"><button type="button" data-chart-zoom="out" data-chart-id="${esc(id)}" title="${esc(tr('history.zoomOut'))}" ${zoom <= 1 ? 'disabled' : ''}>−</button><button type="button" class="chart-zoom-reset" data-chart-zoom="reset" data-chart-id="${esc(id)}" title="${esc(tr('history.zoomReset'))}">${percent}%</button><button type="button" data-chart-zoom="in" data-chart-id="${esc(id)}" title="${esc(tr('history.zoomIn'))}" ${zoom >= MAX_CHART_ZOOM ? 'disabled' : ''}>+</button></div></div><div class="chart-wrap ${compact ? 'compact-chart' : ''}"><canvas id="${esc(id)}" data-chart-zoom="${zoom}" width="1000" height="${compact ? 300 : 420}" tabindex="0" aria-label="${esc(title)}"></canvas><div class="chart-hover-line" hidden></div><div class="chart-selection" hidden></div><div class="chart-tooltip" hidden></div></div><div class="legend" id="${esc(id)}Legend"></div></div>`;
|
||||
return `<div class="panel chart-panel history-chart-card"><div class="chart-title-row"><div><h3>${esc(title)}</h3><p>${esc(hint || '')}</p><small class="chart-hover-hint">${esc(tr('history.hoverHint'))}</small></div><div class="chart-title-actions"><div class="chart-zoom-controls" aria-label="${esc(tr('history.zoomControls'))}"><button type="button" data-chart-zoom="out" data-chart-id="${esc(id)}" title="${esc(tr('history.zoomOut'))}" ${zoom <= 1 ? 'disabled' : ''}>−</button><button type="button" class="chart-zoom-reset" data-chart-zoom="reset" data-chart-id="${esc(id)}" title="${esc(tr('history.zoomReset'))}">${percent}%</button><button type="button" data-chart-zoom="in" data-chart-id="${esc(id)}" title="${esc(tr('history.zoomIn'))}" ${zoom >= MAX_CHART_ZOOM ? 'disabled' : ''}>+</button></div><button type="button" class="chart-fullscreen-button" data-chart-fullscreen="${esc(id)}" title="${esc(tr('history.fullscreen'))}" aria-label="${esc(tr('history.fullscreen'))}">⛶</button></div></div><div class="chart-wrap ${compact ? 'compact-chart' : ''}"><canvas id="${esc(id)}" data-chart-zoom="${zoom}" width="1000" height="${compact ? 300 : 420}" tabindex="0" aria-label="${esc(title)}"></canvas><div class="chart-hover-line" hidden></div><div class="chart-selection" hidden></div><div class="chart-tooltip" hidden></div></div><div class="legend" id="${esc(id)}Legend"></div></div>`;
|
||||
}
|
||||
|
||||
function historySeriesColor(index) {
|
||||
return cssColor(HISTORY_COLORS[index % HISTORY_COLORS.length], `hsl(${(index * 67) % 360} 68% 55%)`);
|
||||
}
|
||||
|
||||
|
||||
document.addEventListener('keydown', event => {
|
||||
if (event.key === 'Escape') closeChartPreview(document.querySelector('.history-chart-card.chart-fullscreen-fallback'));
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user