mirror of
https://github.com/ClusterCockpit/cc-backend
synced 2025-07-26 22:26:08 +02:00
replace plotTable with new bootstrap plotGrid component
- helps with narrow window sizes - plotTable kept for now
This commit is contained in:
60
web/frontend/src/generic/PlotGrid.svelte
Normal file
60
web/frontend/src/generic/PlotGrid.svelte
Normal file
@@ -0,0 +1,60 @@
|
||||
<!--
|
||||
@component Organized display of plots as bootstrap (sveltestrap) grid
|
||||
|
||||
Properties:
|
||||
- `itemsPerRow Number`: Elements to render per row
|
||||
- `items [Any]`: List of plot components to render
|
||||
- `renderFor String`: If 'job', filter disabled metrics
|
||||
-->
|
||||
|
||||
<script>
|
||||
import {
|
||||
Row,
|
||||
Col,
|
||||
} from "@sveltestrap/sveltestrap";
|
||||
|
||||
export let itemsPerRow
|
||||
export let items
|
||||
export let renderFor
|
||||
|
||||
let rows = []
|
||||
let colWidth;
|
||||
const isPlaceholder = x => x._is_placeholder === true
|
||||
|
||||
function tile(items, itemsPerRow) {
|
||||
const rows = []
|
||||
for (let ri = 0; ri < items.length; ri += itemsPerRow) {
|
||||
const row = []
|
||||
for (let ci = 0; ci < itemsPerRow; ci += 1) {
|
||||
if (ri + ci < items.length)
|
||||
row.push(items[ri + ci])
|
||||
else
|
||||
row.push({ _is_placeholder: true, ri, ci })
|
||||
}
|
||||
rows.push(row)
|
||||
}
|
||||
return rows
|
||||
}
|
||||
|
||||
$: if (renderFor === 'job') {
|
||||
rows = tile(items.filter(item => item.disabled === false), itemsPerRow)
|
||||
} else {
|
||||
rows = tile(items, itemsPerRow)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
{#each rows as row}
|
||||
<Row cols={{ xs: 1, sm: 1, md: 2, lg: itemsPerRow}}>
|
||||
{#each row as item (item)}
|
||||
<Col class="px-1">
|
||||
<div bind:clientWidth={colWidth}>
|
||||
{#if !isPlaceholder(item)}
|
||||
<slot item={item} width={colWidth}/>
|
||||
{/if}
|
||||
</div>
|
||||
</Col>
|
||||
{/each}
|
||||
</Row>
|
||||
{/each}
|
||||
|
@@ -549,6 +549,10 @@
|
||||
|
||||
onMount(() => {
|
||||
if (series[0].data.length > 0) {
|
||||
if (forNode) {
|
||||
plotWrapper.style.paddingTop = "0.5rem"
|
||||
plotWrapper.style.paddingBottom = "0.5rem"
|
||||
}
|
||||
plotWrapper.style.backgroundColor = backgroundColor();
|
||||
render();
|
||||
}
|
||||
@@ -562,7 +566,7 @@
|
||||
</script>
|
||||
|
||||
{#if series[0].data.length > 0}
|
||||
<div bind:this={plotWrapper} class="cc-plot"></div>
|
||||
<div bind:this={plotWrapper} class="cc-plot"/>
|
||||
{:else}
|
||||
<Card class="mx-4" body color="warning"
|
||||
>Cannot render plot: No series data returned for <code>{metric}</code></Card
|
||||
|
@@ -46,8 +46,8 @@
|
||||
return;
|
||||
|
||||
const [minX, minY] = [0., 0.];
|
||||
let maxX = X.reduce((maxX, x) => Math.max(maxX, x), minX);
|
||||
let maxY = Y.reduce((maxY, y) => Math.max(maxY, y), minY);
|
||||
let maxX = X ? X.reduce((maxX, x) => Math.max(maxX, x), minX) : 1.0;
|
||||
let maxY = Y ? Y.reduce((maxY, y) => Math.max(maxY, y), minY) : 1.0;
|
||||
const w = width - paddingLeft - paddingRight;
|
||||
const h = height - paddingTop - paddingBottom;
|
||||
|
||||
@@ -68,24 +68,26 @@
|
||||
|
||||
// Draw Data
|
||||
let size = 3
|
||||
if (S) {
|
||||
if (S && X && Y) {
|
||||
let max = S.reduce((max, s, i) => (X[i] == null || Y[i] == null || Number.isNaN(X[i]) || Number.isNaN(Y[i])) ? max : Math.max(max, s), 0)
|
||||
size = (w / 15) / max
|
||||
}
|
||||
|
||||
ctx.fillStyle = color;
|
||||
for (let i = 0; i < X.length; i++) {
|
||||
let x = X[i], y = Y[i];
|
||||
if (x == null || y == null || Number.isNaN(x) || Number.isNaN(y))
|
||||
continue;
|
||||
if (X?.length > 0) {
|
||||
for (let i = 0; i < X.length; i++) {
|
||||
let x = X[i], y = Y[i];
|
||||
if (x == null || y == null || Number.isNaN(x) || Number.isNaN(y))
|
||||
continue;
|
||||
|
||||
const s = S ? S[i] * size : size;
|
||||
const px = getCanvasX(x);
|
||||
const py = getCanvasY(y);
|
||||
const s = S ? S[i] * size : size;
|
||||
const px = getCanvasX(x);
|
||||
const py = getCanvasY(y);
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(px, py, s, 0, Math.PI * 2, false);
|
||||
ctx.fill();
|
||||
ctx.beginPath();
|
||||
ctx.arc(px, py, s, 0, Math.PI * 2, false);
|
||||
ctx.fill();
|
||||
}
|
||||
}
|
||||
|
||||
// Axes
|
||||
|
@@ -397,16 +397,18 @@ function getMetricConfigDeep(metric, cluster, subCluster) {
|
||||
export function convert2uplot(canvasData) {
|
||||
// Prep: Uplot Data Structure
|
||||
let uplotData = [[],[]] // [X, Y1, Y2, ...]
|
||||
// Iterate
|
||||
canvasData.forEach( cd => {
|
||||
if (Object.keys(cd).length == 4) { // MetricHisto Datafromat
|
||||
uplotData[0].push(cd?.max ? cd.max : 0)
|
||||
uplotData[1].push(cd.count)
|
||||
} else { // Default
|
||||
uplotData[0].push(cd.value)
|
||||
uplotData[1].push(cd.count)
|
||||
}
|
||||
})
|
||||
// Iterate if exists
|
||||
if (canvasData) {
|
||||
canvasData.forEach( cd => {
|
||||
if (Object.keys(cd).length == 4) { // MetricHisto Datafromat
|
||||
uplotData[0].push(cd?.max ? cd.max : 0)
|
||||
uplotData[1].push(cd.count)
|
||||
} else { // Default
|
||||
uplotData[0].push(cd.value)
|
||||
uplotData[1].push(cd.count)
|
||||
}
|
||||
})
|
||||
}
|
||||
return uplotData
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user