Svelte Stepper

repository·main·Indexed 41 days ago

https://github.com/efstajas/svelte-stepper

A headless library for building animated, multi-step flows in Svelte. It provides logic for step navigation, context sharing, and side-steps while leaving styling and layout control to the developer. Features include type-safe step definitions via makeStep, programmatic control with createStepController, and customizable transitions using stepIntroTransition and stepOutroTransition.

Tokens
3.3K
Snippets
14
Records
16
Agent score
52%

What's inside Svelte Stepper

  1. Use the Context API to share data between steps

    main

    To share data (like form state) across multiple steps, pass a context prop to the Stepper. This prop should be a function that returns a Svelte writable store.

    Every step component within that stepper will automatically receive a context prop containing that same writable store, allowing steps to read from and write to the shared state.

    // In the parent page
    <script lang="ts">
      import { Stepper, makeStep } from '@efstajas/svelte-stepper';
      import { writable, type Writable } from 'svelte/store';
    
      interface MyContext { foo: string; bar: number; }
    
      const exampleContext = () => writable<MyContext>({ foo: 'bar', bar: 42 });
      const exampleSteps = [ /* ... steps ... */ ];
    </script>
    
    <Stepper steps={exampleSteps} context={exampleContext} />
    
    // Inside a Step component
    <script lang="ts">
      import { createStepController } from '@efstajas/svelte-stepper';
      import type { Writable } from 'svelte/store';
      import type { MyContext } from './parent.svelte';
    
      export let context: Writable<MyContext>;
      const stepController = createStepController();
    </script>
    
    <button on:click={() => context.update(v => ({ ...v, bar: v.bar + 1 }))}>Increment</button>
    <button on:click={stepController.nextStep}>Next</button>
  2. Configure Stepper padding

    main
    To prevent visual issues where steps might overflow the container during transitions, do not wrap the Stepper in a padded element. Instead, pass the padding prop directly to the Stepper component. This ensures the stepper can correctly manage overflow during animations.
  3. Implement Side-Steps for temporary flows

    main

    A side-step allows you to temporarily launch a different set of steps and automatically return to the original flow once the side-step is completed.

    • To start: Call stepController.sidestep(newSteps) from within a step component.
    • To cancel: Call stepController.cancelSidestep() to immediately return to the original flow.
    • To finish: Navigating through the side-step using nextStep() will automatically return the user to the original flow once the side-step sequence ends.
    <script lang="ts">
      import { createStepController, makeStep } from '@efstajas/svelte-stepper';
      import MySidestepStep from './MySidestepStep.svelte';
    
      const stepController = createStepController();
    </script>
    
    <button on:click={() => stepController.sidestep([
      makeStep({ component: MySidestepStep, props: undefined })
    ])}>
      Start sidestep
    </button>
  4. Create a Step component with createStepController

    main

    A step is a standard Svelte component. To allow the component to communicate with the stepper (e.g., to move forward or backward), use the createStepController function to obtain a stepController object. This object provides methods like nextStep and previousStep.

    <script lang="ts">
      import { createStepController } from '@efstajas/svelte-stepper';
    
      const stepController = createStepController();
    </script>
    
    <h1>My first step</h1>
    
    <button on:click={stepController.nextStep}>Next</button>
  5. Handle Stepper events: `stepChange` and `conclusion`

    main

    The Stepper component emits two main events:

    • on:stepChange: Emitted when moving to a new step. The event detail contains newIndex (number), of (total steps), and direction ('forward' or 'backward').
    • on:conclusion: Emitted when the user reaches the end of the flow and calls nextStep() on the last step.
    <Stepper
      steps={exampleSteps}
      on:conclusion={() => {
        alert('Flow finished!');
      }}
      on:stepChange={(e) => {
        const { newIndex, of, direction } = e.detail;
        console.log(`Step ${newIndex + 1} of ${of} (${direction})`);
      }}
    />
  6. Initialize the Stepper component

    main

    The Stepper component is the core of the library. It requires a steps prop, which is an array of step objects created using the makeStep utility. You can pass custom props to each step component via makeStep, and these props are automatically type-safe.

    <script lang="ts">
      import { Stepper, makeStep } from '@efstajas/svelte-stepper';
      import MyFirstStep from './MyFirstStep.svelte';
      import MySecondStepWithProps from './MySecondStepWithProps.svelte';
      import MyFinalStep from './MyFinalStep.svelte';
    
      const exampleSteps = [
        makeStep({
          component: MyFirstStep,
          props: undefined
        }),
        makeStep({
          component: MySecondStepWithProps,
          props: {
            foo: 'bar',
          },
        }),
        makeStep({
          component: MyFinalStep,
          props: undefined
        })
      ];
    </script>
    
    <Stepper steps={exampleSteps} />
  7. Customize Stepper animations and transitions

    main

    Svelte Stepper provides several ways to control animations:

    1. defaultTransitionDuration: A number (defaulting to 300ms) that controls the speed of the container height transition and the step entry/exit transitions.
    2. stepIntroTransition and stepOutroTransition: Use these to provide custom Svelte transition functions. These props accept an object containing:
      • transitionFn: A standard Svelte transition function.
      • params: A function that returns the parameters for the transition. This function receives an optional direction argument ('forward' or 'backward'), allowing you to vary animations based on navigation direction.
    <Stepper
      steps={exampleSteps}
      defaultTransitionDuration={600}
      stepOutroTransition={{
        transitionFn: scale,
        params: () => ({
          duration: 600,
          delay: 0,
          scale: 1,
        }),
      }}
      stepIntroTransition={{
        transitionFn: fly,
        params: (direction) => ({
          duration: 600,
          delay: 0,
          x: direction === 'forward' ? 100 : -100,
        }),
      }}
    />
  8. Handle StepperEvents

    main

    The StepperEvents interface defines the events emitted by the stepper during navigation. You can listen for these to react to user movement or the completion of the entire flow.

    • stepChange: Emitted when the current step changes. Includes newIndex (the index of the new step), of (the total number of steps), and direction ('forward' or 'backward').
    • conclusion: Emitted when the stepper reaches the end of the flow.
    // Example of the shape of stepChange event data
    // {
    //   newIndex: number;
    //   of: number;
    //   direction: 'forward' | 'backward';
    // }
  9. Use StepComponentEvents for navigation control

    main

    When interacting with individual steps, you can trigger navigation using StepComponentEvents. This allows a component within a step to control the stepper's flow:

    • goForward: Moves forward by a specified amount using MovePayload (e.g., { by: 1 }).
    • goBackward: Moves backward by a specified amount using MovePayload (e.g., { by: 1 }).
    • sidestep: Temporarily injects a secondary flow of steps (SidestepPayload) after the current step. Once the sidestep flow concludes, the stepper returns to the original flow.
    • cancelSidestep: Terminates a sidestep flow and returns to the original flow.
    // Example payload for goForward
    // { by: number }
    
    // Example payload for sidestep
    // { steps: Steps }
  10. Use the Stepper component

    main

    The Stepper component is the primary entry point for implementing a stepper UI. It acts as the container for managing and displaying multiple steps.

    import { Stepper } from '@efstajas/svelte-stepper';
    
    // Usage within a Svelte component
    <Stepper>
      <!-- Step content goes here -->
    </Stepper>