Initial migration to Svelte5 via full syntax compatability

- updated all dependencies
- removed svelte-chartjs wrapper from dependencies
- sveltestrap causes compilation warnings (once)
- Header.svelte uses new Svelte5 syntax as example
- fixed most initial compilation warnings except circular dependencies with TBD cause
This commit is contained in:
Christoph Kluge 2025-02-03 17:31:01 +01:00
parent 1e63cdbcda
commit 5681062f01
27 changed files with 561 additions and 471 deletions

File diff suppressed because it is too large Load Diff

View File

@ -7,24 +7,23 @@
"dev": "rollup -c -w" "dev": "rollup -c -w"
}, },
"devDependencies": { "devDependencies": {
"@rollup/plugin-commonjs": "^25.0.8", "@rollup/plugin-commonjs": "^28.0.2",
"@rollup/plugin-node-resolve": "^15.3.0", "@rollup/plugin-node-resolve": "^16.0.0",
"@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-terser": "^0.4.4",
"@timohausmann/quadtree-js": "^1.2.6", "@timohausmann/quadtree-js": "^1.2.6",
"rollup": "^4.27.4", "rollup": "^4.34.1",
"rollup-plugin-css-only": "^4.5.2", "rollup-plugin-css-only": "^4.5.2",
"rollup-plugin-svelte": "^7.2.2", "rollup-plugin-svelte": "^7.2.2",
"svelte": "^4.2.19" "svelte": "^5.19.6"
}, },
"dependencies": { "dependencies": {
"@rollup/plugin-replace": "^5.0.7", "@rollup/plugin-replace": "^6.0.2",
"@sveltestrap/sveltestrap": "^6.2.7", "@sveltestrap/sveltestrap": "^7.0.3",
"@urql/svelte": "^4.2.2", "@urql/svelte": "^4.2.2",
"chart.js": "^4.4.6", "chart.js": "^4.4.7",
"date-fns": "^2.30.0", "date-fns": "^4.1.0",
"graphql": "^16.9.0", "graphql": "^16.10.0",
"mathjs": "^12.4.3", "mathjs": "^14.2.0",
"svelte-chartjs": "^3.1.5",
"uplot": "^1.6.31", "uplot": "^1.6.31",
"wonka": "^6.3.4" "wonka": "^6.3.4"
} }

View File

@ -386,6 +386,7 @@
<Card body color="danger">{$topQuery.error.message}</Card> <Card body color="danger">{$topQuery.error.message}</Card>
{:else} {:else}
<Pie <Pie
type={groupSelection.key}
size={colWidth1} size={colWidth1}
sliceLabel={sortSelection.label} sliceLabel={sortSelection.label}
quantities={$topQuery.data.topList.map( quantities={$topQuery.data.topList.map(

View File

@ -5,6 +5,7 @@
- `username String`: Empty string if auth. is disabled, otherwise the username as string - `username String`: Empty string if auth. is disabled, otherwise the username as string
- `authlevel Number`: The current users authentication level - `authlevel Number`: The current users authentication level
- `clusters [String]`: List of cluster names - `clusters [String]`: List of cluster names
- `subClusters [String]`: List of subCluster names
- `roles [Number]`: Enum containing available roles - `roles [Number]`: Enum containing available roles
--> -->
@ -23,14 +24,15 @@
import NavbarLinks from "./header/NavbarLinks.svelte"; import NavbarLinks from "./header/NavbarLinks.svelte";
import NavbarTools from "./header/NavbarTools.svelte"; import NavbarTools from "./header/NavbarTools.svelte";
export let username; let { username, authlevel, clusters, subClusters, roles } = $props();
export let authlevel;
export let clusters;
export let subClusters;
export let roles;
let isOpen = false; let isOpen = $state(false);
let screenSize; let screenSize = $state(0);
let showMax = $derived(screenSize >= 1500);
let showMid = $derived(screenSize < 1500 && screenSize >= 1300);
let showSml = $derived(screenSize < 1300 && screenSize >= 768);
let showBrg = $derived(screenSize < 768);
const jobsTitle = new Map(); const jobsTitle = new Map();
jobsTitle.set(2, "Job Search"); jobsTitle.set(2, "Job Search");
@ -123,26 +125,28 @@
</script> </script>
<svelte:window bind:innerWidth={screenSize} /> <svelte:window bind:innerWidth={screenSize} />
<Navbar color="light" light expand="md" fixed="top"> <Navbar color="light" light expand="md" fixed="top">
<NavbarBrand href="/"> <NavbarBrand href="/">
<img alt="ClusterCockpit Logo" src="/img/logo.png" height="25rem" /> <img alt="ClusterCockpit Logo" src="/img/logo.png" height="25rem" />
</NavbarBrand> </NavbarBrand>
<NavbarToggler on:click={() => (isOpen = !isOpen)} /> <NavbarToggler onclick={() => (isOpen = !isOpen)} />
<Collapse <Collapse
style="justify-content: space-between" style="justify-content: space-between"
{isOpen} {isOpen}
navbar navbar
expand="md" expand="md"
on:update={({ detail }) => (isOpen = detail.isOpen)} onupdate={({ detail }) => (isOpen = detail.isOpen)}
> >
<Nav navbar> <Nav navbar>
{#if screenSize > 1500 || screenSize < 768} {#if showMax || showBrg}
<NavbarLinks <NavbarLinks
{clusters} {clusters}
{subClusters} {subClusters}
links={views.filter((item) => item.requiredRole <= authlevel)} links={views.filter((item) => item.requiredRole <= authlevel)}
/> />
{:else if screenSize > 1300}
{:else if showMid}
<NavbarLinks <NavbarLinks
{clusters} {clusters}
{subClusters} {subClusters}
@ -169,7 +173,8 @@
</DropdownMenu> </DropdownMenu>
</Dropdown> </Dropdown>
{/if} {/if}
{:else}
{:else if showSml}
<NavbarLinks <NavbarLinks
{clusters} {clusters}
{subClusters} {subClusters}
@ -228,8 +233,11 @@
</DropdownMenu> </DropdownMenu>
</Dropdown> </Dropdown>
{/if} {/if}
{:else}
<span>Error: Unknown Window Size!</span>
{/if} {/if}
</Nav> </Nav>
<NavbarTools {username} {authlevel} {roles} {screenSize} /> <NavbarTools {username} {authlevel} {roles} {screenSize} />
</Collapse> </Collapse>
</Navbar> </Navbar>

View File

@ -478,6 +478,7 @@
<Card body color="danger">{$topUserQuery.error.message}</Card> <Card body color="danger">{$topUserQuery.error.message}</Card>
{:else} {:else}
<Pie <Pie
canvasId="hpcpie-users"
size={colWidth} size={colWidth}
sliceLabel={topUserSelection.label} sliceLabel={topUserSelection.label}
quantities={$topUserQuery.data.topUser.map( quantities={$topUserQuery.data.topUser.map(
@ -538,6 +539,7 @@
<Card body color="danger">{$topProjectQuery.error.message}</Card> <Card body color="danger">{$topProjectQuery.error.message}</Card>
{:else} {:else}
<Pie <Pie
canvasId="hpcpie-projects"
size={colWidth} size={colWidth}
sliceLabel={topProjectSelection.label} sliceLabel={topProjectSelection.label}
quantities={$topProjectQuery.data.topProjects.map( quantities={$topProjectQuery.data.topProjects.map(

View File

@ -1,9 +1,10 @@
import { mount } from 'svelte';
import {} from './header.entrypoint.js' import {} from './header.entrypoint.js'
import Analysis from './Analysis.root.svelte' import Analysis from './Analysis.root.svelte'
filterPresets.cluster = cluster filterPresets.cluster = cluster
new Analysis({ mount(Analysis, {
target: document.getElementById('svelte-app'), target: document.getElementById('svelte-app'),
props: { props: {
filterPresets: filterPresets, filterPresets: filterPresets,

View File

@ -1,7 +1,8 @@
import { mount } from 'svelte';
import {} from './header.entrypoint.js' import {} from './header.entrypoint.js'
import Config from './Config.root.svelte' import Config from './Config.root.svelte'
new Config({ mount(Config, {
target: document.getElementById('svelte-app'), target: document.getElementById('svelte-app'),
props: { props: {
isAdmin: isAdmin, isAdmin: isAdmin,

View File

@ -153,13 +153,13 @@
{/each} {/each}
</div> </div>
<p style="display: flex; align-items: center;"> <p style="display: flex; align-items: center;">
<Button type="submit" color="primary">Submit</Button> <Button type="submit" color="primary" style="margin-right: 1.5em;">Submit</Button>
{#if displayMessage}<div style="margin-left: 1.5em;"> {#if displayMessage}
<b <b
><code style="color: {message.color};" out:fade>{message.msg}</code ><code style="color: {message.color};" out:fade>{message.msg}</code
></b ></b
> >
</div>{/if} {/if}
</p> </p>
</form> </form>
</Card> </Card>

View File

@ -91,15 +91,15 @@
</span> </span>
{#if job.metaData?.jobName} {#if job.metaData?.jobName}
{#if job.metaData?.jobName.length <= 25} {#if job.metaData?.jobName.length <= 25}
<div>{job.metaData.jobName}</div> <span>{job.metaData.jobName}</span>
{:else} {:else}
<div <span
class="truncate" class="truncate"
style="cursor:help;" style="cursor:help;"
title={job.metaData.jobName} title={job.metaData.jobName}
> >
{job.metaData.jobName} {job.metaData.jobName}
</div> </span>
{/if} {/if}
{/if} {/if}
{#if job.arrayJobId} {#if job.arrayJobId}

View File

@ -28,13 +28,13 @@
</div> </div>
<div class="cc-pagination-right"> <div class="cc-pagination-right">
{#if !backButtonDisabled} {#if !backButtonDisabled}
<button class="reset nav" type="button" <button aria-label="page-reset" class="reset nav" type="button"
on:click|preventDefault="{reset}"></button> on:click|preventDefault="{reset}"></button>
<button class="left nav" type="button" <button aria-label="page-back" class="left nav" type="button"
on:click|preventDefault="{() => { page -= 1; }}"></button> on:click|preventDefault="{() => { page -= 1; }}"></button>
{/if} {/if}
{#if !nextButtonDisabled} {#if !nextButtonDisabled}
<button class="right nav" type="button" <button aria-label="page-up" class="right nav" type="button"
on:click|preventDefault="{() => { page += 1; }}"></button> on:click|preventDefault="{() => { page += 1; }}"></button>
{/if} {/if}
</div> </div>

View File

@ -259,7 +259,7 @@
<!-- Define Wrapper and NoData Card within $width --> <!-- Define Wrapper and NoData Card within $width -->
<div bind:clientWidth={width}> <div bind:clientWidth={width}>
{#if data.length > 0} {#if data.length > 0}
<div bind:this={plotWrapper} /> <div bind:this={plotWrapper}></div>
{:else} {:else}
<Card class="mx-4" body color="warning" <Card class="mx-4" body color="warning"
>Cannot render histogram: No data!</Card >Cannot render histogram: No data!</Card

View File

@ -607,7 +607,7 @@
{#if series[0]?.data && series[0].data.length > 0} {#if series[0]?.data && series[0].data.length > 0}
<div bind:this={plotWrapper} bind:clientWidth={width} <div bind:this={plotWrapper} bind:clientWidth={width}
style="background-color: {backgroundColor()};" class={forNode ? 'py-2 rounded' : 'rounded'} style="background-color: {backgroundColor()};" class={forNode ? 'py-2 rounded' : 'rounded'}
/> ></div>
{:else} {:else}
<Card body color="warning" class="mx-4" <Card body color="warning" class="mx-4"
>Cannot render plot: No series data returned for <code>{metric}</code></Card >Cannot render plot: No series data returned for <code>{metric}</code></Card

View File

@ -1,5 +1,5 @@
<!-- <!--
@component Pie Plot based on uPlot Pie @component Pie Plot based on chart.js Pie
Properties: Properties:
- `size Number`: X and Y size of the plot, for square shape - `size Number`: X and Y size of the plot, for square shape
@ -31,33 +31,17 @@
] ]
</script> </script>
<script> <script>
import { Pie } from 'svelte-chartjs'; import Chart from 'chart.js/auto'
import { import { onMount } from 'svelte';
Chart as ChartJS,
Title,
Tooltip,
Legend,
Filler,
ArcElement,
CategoryScale
} from 'chart.js';
ChartJS.register(
Title,
Tooltip,
Legend,
Filler,
ArcElement,
CategoryScale
);
export let canvasId
export let size export let size
export let sliceLabel export let sliceLabel
export let quantities export let quantities
export let entities export let entities
export let displayLegend = false export let displayLegend = false
$: data = { const data = {
labels: entities, labels: entities,
datasets: [ datasets: [
{ {
@ -79,10 +63,22 @@
} }
} }
onMount(() => {
new Chart(
document.getElementById(canvasId),
{
type: 'pie',
data: data,
options: options
}
);
});
</script> </script>
<!-- <div style="width: 500px;"><canvas id="dimensions"></canvas></div><br/> -->
<div class="chart-container" style="--container-width: {size}; --container-height: {size}"> <div class="chart-container" style="--container-width: {size}; --container-height: {size}">
<Pie {data} {options}/> <canvas id={canvasId}></canvas>
</div> </div>
<style> <style>

View File

@ -1,5 +1,5 @@
<!-- <!--
@component Polar Plot based on chartJS Radar @component Polar Plot based on chart.js Radar
Properties: Properties:
- `footprintData [Object]?`: job.footprint content, evaluated in regards to peak config in jobSummary.svelte [Default: null] - `footprintData [Object]?`: job.footprint content, evaluated in regards to peak config in jobSummary.svelte [Default: null]
@ -11,29 +11,10 @@
--> -->
<script> <script>
import { getContext } from 'svelte' import { getContext, onMount } from 'svelte'
import { Radar } from 'svelte-chartjs'; import Chart from 'chart.js/auto'
import {
Chart as ChartJS,
Title,
Tooltip,
Legend,
Filler,
PointElement,
RadialLinearScale,
LineElement
} from 'chart.js';
ChartJS.register(
Title,
Tooltip,
Legend,
Filler,
PointElement,
RadialLinearScale,
LineElement
);
export let canvasId;
export let footprintData = null; export let footprintData = null;
export let metrics = null; export let metrics = null;
export let cluster = null; export let cluster = null;
@ -183,10 +164,23 @@
} }
} }
onMount(() => {
new Chart(
document.getElementById(canvasId),
{
type: 'radar',
data: data,
options: options,
height: height
}
);
});
</script> </script>
<!-- <div style="width: 500px;"><canvas id="dimensions"></canvas></div><br/> -->
<div class="chart-container"> <div class="chart-container">
<Radar {data} {options} {height}/> <canvas id={canvasId}></canvas>
</div> </div>
<style> <style>

View File

@ -363,7 +363,7 @@
</script> </script>
{#if data != null} {#if data != null}
<div bind:this={plotWrapper} class="p-2"/> <div bind:this={plotWrapper} class="p-2"></div>
{:else} {:else}
<Card class="mx-4" body color="warning">Cannot render roofline: No data!</Card <Card class="mx-4" body color="warning">Cannot render roofline: No data!</Card
> >

View File

@ -148,10 +148,13 @@
<li <li
class="cc-config-column list-group-item" class="cc-config-column list-group-item"
draggable={true} draggable={true}
ondragover="return false" ondragover={() => false}
on:dragstart={(event) => columnsDragStart(event, index)} ondragstart={(event) => columnsDragStart(event, index)}
on:drop|preventDefault={(event) => columnsDrag(event, index)} ondrop={(event) => {
on:dragenter={() => (columnHovering = index)} event.preventDefault()
columnsDrag(event, index)
}}
ondragenter={() => (columnHovering = index)}
class:is-active={columnHovering === index} class:is-active={columnHovering === index}
> >
{#if unorderedMetrics.includes(metric)} {#if unorderedMetrics.includes(metric)}

View File

@ -1,10 +1,11 @@
import Header from './Header.svelte' import { mount } from 'svelte';
import Header from './Header.svelte';
const headerDomTarget = document.getElementById('svelte-header') const headerDomTarget = document.getElementById('svelte-header');
if (headerDomTarget != null) { if (headerDomTarget != null) {
new Header({ mount(Header, {
target: headerDomTarget, target: headerDomTarget,
props: { ...header }, props: { ...header },
}) });
} }

View File

@ -1,7 +1,8 @@
import { mount } from 'svelte';
import {} from './header.entrypoint.js' import {} from './header.entrypoint.js'
import Job from './Job.root.svelte' import Job from './Job.root.svelte'
new Job({ mount(Job, {
target: document.getElementById('svelte-app'), target: document.getElementById('svelte-app'),
props: { props: {
dbid: jobInfos.id, dbid: jobInfos.id,

View File

@ -282,6 +282,7 @@
<TabPane tabId="polar" tab="Polar" active={!showFootprint}> <TabPane tabId="polar" tab="Polar" active={!showFootprint}>
<CardBody> <CardBody>
<Polar <Polar
canvasId={job.jobId}
{footprintData} {footprintData}
{jobMetrics} {jobMetrics}
/> />

View File

@ -71,26 +71,28 @@
{:else} {:else}
<td colspan="4"> <td colspan="4">
<table style="width: 100%;"> <table style="width: 100%;">
<tr> <tbody>
{#each ["id", "min", "avg", "max"] as field}
<th on:click={() => sortByField(field)}>
Sort
<Icon
name="caret-{sorting[field].dir}{sorting[field].active
? '-fill'
: ''}"
/>
</th>
{/each}
</tr>
{#each series as s, i}
<tr> <tr>
<th>{s.id ?? i}</th> {#each ["id", "min", "avg", "max"] as field}
<td>{s.statistics.min}</td> <th on:click={() => sortByField(field)}>
<td>{s.statistics.avg}</td> Sort
<td>{s.statistics.max}</td> <Icon
name="caret-{sorting[field].dir}{sorting[field].active
? '-fill'
: ''}"
/>
</th>
{/each}
</tr> </tr>
{/each} {#each series as s, i}
<tr>
<th>{s.id ?? i}</th>
<td>{s.statistics.min}</td>
<td>{s.statistics.avg}</td>
<td>{s.statistics.max}</td>
</tr>
{/each}
</tbody>
</table> </table>
</td> </td>
{/if} {/if}

View File

@ -1,7 +1,8 @@
import { mount } from 'svelte';
import {} from './header.entrypoint.js' import {} from './header.entrypoint.js'
import Jobs from './Jobs.root.svelte' import Jobs from './Jobs.root.svelte'
new Jobs({ mount(Jobs, {
target: document.getElementById('svelte-app'), target: document.getElementById('svelte-app'),
props: { props: {
filterPresets: filterPresets, filterPresets: filterPresets,

View File

@ -1,7 +1,8 @@
import { mount } from 'svelte';
import {} from './header.entrypoint.js' import {} from './header.entrypoint.js'
import List from './List.root.svelte' import List from './List.root.svelte'
new List({ mount(List, {
target: document.getElementById('svelte-app'), target: document.getElementById('svelte-app'),
props: { props: {
filterPresets: filterPresets, filterPresets: filterPresets,

View File

@ -1,7 +1,8 @@
import { mount } from 'svelte';
import {} from './header.entrypoint.js' import {} from './header.entrypoint.js'
import Node from './Node.root.svelte' import Node from './Node.root.svelte'
new Node({ mount(Node, {
target: document.getElementById('svelte-app'), target: document.getElementById('svelte-app'),
props: { props: {
cluster: infos.cluster, cluster: infos.cluster,

View File

@ -1,7 +1,8 @@
import { mount } from 'svelte';
import {} from './header.entrypoint.js' import {} from './header.entrypoint.js'
import Status from './Status.root.svelte' import Status from './Status.root.svelte'
new Status({ mount(Status, {
target: document.getElementById('svelte-app'), target: document.getElementById('svelte-app'),
props: { props: {
cluster: infos.cluster, cluster: infos.cluster,

View File

@ -1,7 +1,8 @@
import { mount } from 'svelte';
import {} from './header.entrypoint.js' import {} from './header.entrypoint.js'
import Systems from './Systems.root.svelte' import Systems from './Systems.root.svelte'
new Systems({ mount(Systems, {
target: document.getElementById('svelte-app'), target: document.getElementById('svelte-app'),
props: { props: {
displayType: displayType, displayType: displayType,

View File

@ -155,7 +155,7 @@
height={175} height={175}
forNode forNode
/> />
<div class="my-2"/> <div class="my-2"></div>
{#key extendedLegendData} {#key extendedLegendData}
<MetricPlot <MetricPlot
{cluster} {cluster}

View File

@ -1,7 +1,8 @@
import { mount } from 'svelte';
import {} from './header.entrypoint.js' import {} from './header.entrypoint.js'
import User from './User.root.svelte' import User from './User.root.svelte'
new User({ mount(User, {
target: document.getElementById('svelte-app'), target: document.getElementById('svelte-app'),
props: { props: {
filterPresets: filterPresets, filterPresets: filterPresets,