diff --git a/src/lib/components/Spinner.svelte b/src/lib/components/Spinner.svelte new file mode 100644 index 0000000000000000000000000000000000000000..0732f2b7dd37f53a3bb0cc2a315e5d1bfb304e83 --- /dev/null +++ b/src/lib/components/Spinner.svelte @@ -0,0 +1,64 @@ +<script lang="ts"> + // Code inspired from: https://github.com/Schum123/svelte-loading-spinners/blob/master/src/Circle2.svelte + // MIT license. + + export let size: string | number = "60" + export let unit: string = "px" + export let colorOuter: string = "#FF3E00" + export let colorCenter: string = "#40B3FF" + export let colorInner: string = "#676778" + export let durationMultiplier: number = 1 + export let durationOuter: string = `${durationMultiplier * 2}s` + export let durationInner: string = `${durationMultiplier * 1.5}s` + export let durationCenter: string = `${durationMultiplier * 3}s` +</script> + +<div + class="circle mx-auto my-8" + style="--size: {size}{unit}; --colorInner: {colorInner}; --colorCenter: {colorCenter}; --colorOuter: {colorOuter}; --durationInner: {durationInner}; --durationCenter: {durationCenter}; --durationOuter: {durationOuter};" +/> + +<style> + .circle { + width: var(--size); + height: var(--size); + box-sizing: border-box; + position: relative; + border: 3px solid transparent; + border-top-color: var(--colorOuter); + border-radius: 50%; + animation: circleSpin var(--durationOuter) linear infinite; + } + .circle:before, + .circle:after { + content: ""; + box-sizing: border-box; + position: absolute; + border: 3px solid transparent; + border-radius: 50%; + } + .circle:after { + border-top-color: var(--colorInner); + top: 9px; + left: 9px; + right: 9px; + bottom: 9px; + animation: circleSpin var(--durationInner) linear infinite; + } + .circle:before { + border-top-color: var(--colorCenter); + top: 3px; + left: 3px; + right: 3px; + bottom: 3px; + animation: circleSpin var(--durationCenter) linear infinite; + } + @keyframes circleSpin { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } + } +</style> diff --git a/src/lib/components/WaterfallView.svelte b/src/lib/components/WaterfallView.svelte index a3f4fd32d609e36893c2055783b933221718cf1e..c9bd80cf14d2d8ea009f13b1ccf6e062d1d062f1 100644 --- a/src/lib/components/WaterfallView.svelte +++ b/src/lib/components/WaterfallView.svelte @@ -4,8 +4,9 @@ import type { Writable } from "svelte/store" import { goto } from "$app/navigation" - import { session } from "$app/stores" + import Spinner from "$lib/components/Spinner.svelte" import type { + CalculationName, DecompositionByName, EvaluationByName, VisibleDecomposition, @@ -27,6 +28,9 @@ export let year: number const adaptAmountsScale = getContext("adaptAmountsScale") as Writable<boolean> + const calculationRunningByName = getContext( + "calculationRunningByName", + ) as Writable<{ [name in CalculationName]?: boolean }> const dateFormatter = new Intl.DateTimeFormat("fr-FR", { dateStyle: "full" }) const deltaFormatter = new Intl.NumberFormat("fr-FR", { currency: "EUR", @@ -145,7 +149,12 @@ </script> {#if visibleDecompositions.length > 0} - <div class="flex pr-2 mt-4"> + <div class="flex mt-4 pr-2 relative"> + {#if Object.keys($calculationRunningByName).length > 0} + <div class="absolute inset-0 bg-gray-300 bg-opacity-50 z-50"> + <Spinner /> + </div> + {/if} <!-- partie gauche dispositifs et ticket de caisse--> <div class="bg-opacity-40 shadow-lg w-4/5"> <div class="flex justify-between"> diff --git a/src/lib/components/test_cases/TestCaseView.svelte b/src/lib/components/test_cases/TestCaseView.svelte index 3060bb9a301158fe8ddf33eca9b8545d1a25b2ce..c9f104025d29c41965b684b8c773c58f4ad56cd7 100644 --- a/src/lib/components/test_cases/TestCaseView.svelte +++ b/src/lib/components/test_cases/TestCaseView.svelte @@ -7,6 +7,7 @@ import PictoArbreMetropole from "$lib/components/pictos/PictoArbreMetropole.svelte" import PictoFemme from "$lib/components/pictos/PictoFemme.svelte" import PictoEnfant from "$lib/components/pictos/PictoEnfant.svelte" + import Spinner from "$lib/components/Spinner.svelte" import ValueChange from "$lib/components/test_cases/ValueChange.svelte" import WaterfallView from "$lib/components/WaterfallView.svelte" import type { @@ -41,6 +42,9 @@ const advanced = $session.advanced let axisDescription: AxisDescription | null = null const billName = getContext("billName") as Writable<string | undefined> + const calculationRunningByName = getContext( + "calculationRunningByName", + ) as Writable<{ [name in CalculationName]?: boolean }> const childrenKey = $session.childrenKey const dispatch = createEventDispatcher() const enfantVariablesName = ["age"] @@ -411,8 +415,13 @@ {/if} </div> <div - class="px-4 py-2 bg-gray-100 grid grid-flow-col grid-cols-{waterfalls.length} grid-rows-3 gap-x-4 " + class="px-4 py-2 bg-gray-100 grid grid-flow-col grid-cols-{waterfalls.length} grid-rows-3 gap-x-4 relative" > + {#if Object.keys($calculationRunningByName).length > 0} + <div class="absolute inset-0 bg-gray-300 bg-opacity-50 z-50"> + <Spinner /> + </div> + {/if} {#each waterfalls as { icon, label, root, total, totalLabel }} <div class="place-self-center"> {#if icon !== undefined} diff --git a/src/routes/__layout.svelte b/src/routes/__layout.svelte index 05cbfe6e92b5944c24f45f7a7a81b13af90ac426..1a680a773a4e242ef35150d6b222acf0675fdf6c 100644 --- a/src/routes/__layout.svelte +++ b/src/routes/__layout.svelte @@ -49,6 +49,11 @@ const calculationName: Writable<CalculationName> = writable("law") setContext("calculationName", calculationName) + const calculationRunningByName: Writable<{ + [name in CalculationName]?: boolean + }> = writable({}) + setContext("calculationRunningByName", calculationRunningByName) + const calculationTokenByName: Writable<{ [name in CalculationName]?: string }> = writable({}) @@ -340,6 +345,9 @@ console.log( `Ignoring API ${calculationName} response with invalid token: ${result.token} instead of ${$calculationTokenByName[calculationName]}`, ) + } else if (result.done) { + $calculationRunningByName = { ...$calculationRunningByName } + delete $calculationRunningByName[calculationName] } else { // Count total population of test cases. const entity = entityByKey[result.entity] diff --git a/src/routes/index.svelte b/src/routes/index.svelte index 3d9d1e9d0b7aa179e775b184ba66a8646f397366..481aa3d0ba20aa74b5ecb261ef4d25680c75b073 100644 --- a/src/routes/index.svelte +++ b/src/routes/index.svelte @@ -68,6 +68,9 @@ const calculationName = getContext( "calculationName", ) as Writable<CalculationName> + const calculationRunningByName = getContext( + "calculationRunningByName", + ) as Writable<{ [name in CalculationName]?: boolean }> const calculationTokenByName = getContext( "calculationTokenByName", ) as Writable<{ [name in CalculationName]?: string }> @@ -556,6 +559,10 @@ } if (requestedCalculationsName.has("law") && $webSocketOpenByName.law) { const token = ($calculationTokenByName.law = uuidv4()) + $calculationRunningByName = { + ...$calculationRunningByName, + law: true, + } $webSocketByName.law.send( JSON.stringify({ ...message, title: "law", token }), ) @@ -566,6 +573,10 @@ $webSocketOpenByName.bill ) { const token = ($calculationTokenByName.bill = uuidv4()) + $calculationRunningByName = { + ...$calculationRunningByName, + bill: true, + } $webSocketByName.bill.send( JSON.stringify({ ...message, @@ -574,6 +585,11 @@ token, }), ) + } else if ($calculationRunningByName.bill) { + $calculationRunningByName = { + ...$calculationRunningByName, + } + delete $calculationRunningByName.bill } if ( requestedCalculationsName.has("amendment") && @@ -581,6 +597,10 @@ $webSocketOpenByName.amendment ) { const token = ($calculationTokenByName.amendment = uuidv4()) + $calculationRunningByName = { + ...$calculationRunningByName, + amendment: true, + } $webSocketByName.amendment.send( JSON.stringify({ ...message, @@ -590,6 +610,11 @@ token, }), ) + } else if ($calculationRunningByName.amendment) { + $calculationRunningByName = { + ...$calculationRunningByName, + } + delete $calculationRunningByName.amendment } }