progress-barsvelte

Updating steps of a StepProgress component


I'm learning svelteKit and I'm building a step progress using props so, this is my componente progressStep.svelte

<script>
    export let steps = [];
    export let currentStep = 1;

    const isActive = (index) => {
        console.log(`isActive - index: ${index}, currentStep: ${currentStep}`);
        return index + 1 === currentStep;
    };

    const isCompleted = (index) => {
        console.log(`isCompleted - index: ${index}, currentStep: ${currentStep}`);
        return index + 1 < currentStep;
    };
</script>

<style>
    .progress-container {
        display: flex;
        justify-content: space-between;  
        align-items: center;
        position: relative;
        margin-bottom: 20px;
    }

    .progress-container::before {
        content: '';
        position: absolute;
        top: 50%;
        left: 0;
        right: 0;
        height: 2px;
        background: #ddd;
        z-index: 0;
    }

    .progress-step {
        position: relative;
        z-index: 1;
        display: flex;
        flex-direction: column;
        align-items: center;
    }

    .step-circle {
        width: 30px;
        height: 30px;
        border-radius: 50%;
        display: flex;
        align-items: center;
        justify-content: center;
        background: #ddd;
    }

    .step-active .step-circle {
        background: #007bff;
        color: white;
    }

    .step-completed .step-circle {
        background: #28a745;
        color: white;
    }

    .progress-line {
        position: absolute;
        top: 50%;
        left: 50%;
        height: 2px;
        background: #28a745;
        z-index: 0;
    }
</style>
  
<div class="progress-container">
    {#each steps as step, index}
        <div class="progress-step {isActive(index) ? 'step-active' : ''} {isCompleted(index) ? 'step-completed' : ''}">
            <div class="step-circle">{isCompleted(index) ? '✓' : index + 1}</div>
            <div>{step}</div>
            {#if index < steps.length - 1}
                <div class="progress-line" style="width: {isCompleted(index) || isActive(index) ? 'calc(100% - 15px)' : '0'};"></div>
            {/if}
        </div>
    {/each}
</div>

and I import this component on +page.svelte

<script>
  import ProgressStep from '@/components/progressStep/progressStep.svelte';
  let currentStep = 1;
     
  function nextStep() { 
    if (currentStep < 4) currentStep += 1; 
  }

  function prevStep() {
    if (currentStep > 1) currentStep -= 1;
  }
 
</script>

<section class="section mt-4">
    <div class="col-xs-12">
        <div class="col-6 my-4">
          <ProgressStep currentStep={currentStep} steps={['Step 1', 'Step 2', 'Step 3', 'Step 4']} />

          <div class="btn-group">
            <button class="btn btn-secondary" on:click={()=>prevStep()}>Previous</button>
            <button class="btn btn-primary" on:click={()=>nextStep()}>Next</button>
          </div>
        </div>
    </div>
</section>

I want to build one step progress bar like codepen.io

it's about 4 step progress, as it progresses, the line that connects each step must change color, so currently I'm getting some issue the progress don't work correctly, what's wrong?


Solution

  • The main issue here is that the functions have a "hidden" dependency, so they will not be reevaluated once the step changes.

    This can be fixed by declaring the functions reactively, changes to variables in the function body will cause the function to be re-defined and then called if used in markup (or other reactive code).

    $: isActive = (index) => {
        console.log(`isActive - index: ${index}, currentStep: ${currentStep}`);
        return index + 1 === currentStep;
    };
    
    $: isCompleted = (index) => {
        console.log(`isCompleted - index: ${index}, currentStep: ${currentStep}`);
        return index + 1 < currentStep;
    };
    

    Since these were already arrow functions, you just need to replace const with $:.