feat: add spinning icon

This commit is contained in:
2026-05-07 17:01:44 +02:00
parent 308626bcdc
commit a4f51efead
2 changed files with 65 additions and 7 deletions
+38 -7
View File
@@ -29,6 +29,7 @@
import { tutorialConfig } from '$lib/tutorial/tutorial-config'; import { tutorialConfig } from '$lib/tutorial/tutorial-config';
import { Planty } from '@nodarium/planty'; import { Planty } from '@nodarium/planty';
import type { Graph, NodeInstance } from '@nodarium/types'; import type { Graph, NodeInstance } from '@nodarium/types';
import { Spinner } from '@nodarium/ui';
import { createPerformanceStore } from '@nodarium/utils'; import { createPerformanceStore } from '@nodarium/utils';
import type { Group } from 'three'; import type { Group } from 'three';
@@ -69,6 +70,7 @@
let activeNode = $state<NodeInstance | undefined>(undefined); let activeNode = $state<NodeInstance | undefined>(undefined);
let scene = $state<Group>(null!); let scene = $state<Group>(null!);
let isExecuting = $state(false);
let sidebarOpen = $state(false); let sidebarOpen = $state(false);
let graphInterface = $state<ReturnType<typeof GraphInterface>>(null!); let graphInterface = $state<ReturnType<typeof GraphInterface>>(null!);
@@ -101,10 +103,16 @@
} }
}); });
let timeout: ReturnType<typeof setTimeout>;
async function update( async function update(
g: Graph, g: Graph,
s: Record<string, unknown> = $state.snapshot(graphSettings) s: Record<string, unknown> = $state.snapshot(graphSettings)
) { ) {
if (timeout) clearTimeout(timeout);
timeout = setTimeout(() => {
isExecuting = true;
}, 100);
performanceStore.startRun(); performanceStore.startRun();
try { try {
let a = performance.now(); let a = performance.now();
@@ -129,6 +137,8 @@
} catch (error) { } catch (error) {
console.log('errors', error); console.log('errors', error);
} finally { } finally {
clearTimeout(timeout);
isExecuting = false;
performanceStore.stopRun(); performanceStore.stopRun();
} }
} }
@@ -248,13 +258,20 @@
<header></header> <header></header>
<Grid.Row> <Grid.Row>
<Grid.Cell> <Grid.Cell>
<Viewer <div class="viewer-cell">
bind:scene <Viewer
bind:this={viewerComponent} bind:scene
perf={performanceStore} bind:this={viewerComponent}
debugData={debugData} perf={performanceStore}
centerCamera={appSettings.value.centerCamera} debugData={debugData}
/> centerCamera={appSettings.value.centerCamera}
/>
{#if isExecuting}
<div class="viewer-spinner" aria-label="Executing graph">
<Spinner size={28} />
</div>
{/if}
</div>
</Grid.Cell> </Grid.Cell>
<Grid.Cell> <Grid.Cell>
{#if pm.graph} {#if pm.graph}
@@ -399,6 +416,20 @@
grid-template-rows: 0px 1fr; grid-template-rows: 0px 1fr;
} }
.viewer-cell {
position: relative;
height: 100%;
}
.viewer-spinner {
position: absolute;
bottom: 12px;
right: 12px;
color: var(--color-text, #cecece);
opacity: 0.6;
pointer-events: none;
}
.wrapper :global(canvas) { .wrapper :global(canvas) {
transition: opacity 0.3s ease; transition: opacity 0.3s ease;
opacity: 1; opacity: 1;
+27
View File
@@ -0,0 +1,27 @@
<script lang="ts">
interface Props {
size?: number;
class?: string;
}
let { size = 20, class: _class = '' }: Props = $props();
</script>
<svg
class="animate-spin text-text shrink-0 {_class}"
width={size}
height={size}
viewBox="0 0 24 24"
fill="none"
aria-label="Loading"
role="status"
>
<circle
cx="12"
cy="12"
r="10"
stroke="currentColor"
stroke-width="2.5"
stroke-linecap="round"
stroke-dasharray="40 20"
/>
</svg>