# Task Steps

Ordered task progress with pending, active, completed, and failed states plus settled screen-reader announcements.

- Package: `@mielui/svelte`
- Component version: `1.0.0`
- Depends on Mielui components: _internal/utils, spinner
- Shared utilities: utils.cn, utils.createContext

## Install

~~~~sh
pnpm dlx @mielui/svelte add task-steps
~~~~

## API

This reference is generated at build time from the component manifest, public `index.ts`, and documentation examples below. Changes to those source files are reflected here in the published Markdown. Standard Svelte and HTML attributes accepted by the exported prop types are supported.


### Indicator

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| status | TaskStepStatus \| undefined | - | No | No | State of the step. |

Also accepts the native HTML attributes and events listed in the rendered API reference.

### Item

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| status | TaskStepStatus \| undefined | 'pending' | No | No | State of the step. Indicator, Label, and Meta read it. |

Also accepts the native HTML attributes and events listed in the rendered API reference.

### Label

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |

Also accepts the native HTML attributes and events listed in the rendered API reference.

### List

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |

Also accepts the native HTML attributes and events listed in the rendered API reference.

### Meta

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |

Also accepts the native HTML attributes and events listed in the rendered API reference.

### Summary

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| children | Snippet<[TaskStepsState]> \| undefined | - | No | No | Content rendered inside. |

Also accepts the native HTML attributes and events listed in the rendered API reference.

### TaskSteps

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| labels | TaskStepsLabels \| undefined | - | No | No | Overrides the built-in text and accessible names. Every key is optional and English is the fallback. |
| children | Snippet<[TaskStepsState]> \| undefined | - | No | No | Content rendered inside. |
| steps | TaskStep[] | - | Yes | No | Steps to show, in order. |
| current | number | - | Yes | No | Index of the step in progress. |
| failed | boolean \| undefined | false | No | No | Marks the current step as failed. |
| class | (string & (ClassValue \| null)) \| undefined | - | No | No | Classes added to the element. |

Also accepts the native HTML attributes and events listed in the rendered API reference.

### Public types

~~~~ts
import type { Snippet } from 'svelte';
import type { HTMLAttributes } from 'svelte/elements';
import TaskSteps from './task-steps.svelte';
import Indicator from './task-steps-indicator.svelte';
import Item from './task-steps-item.svelte';
import Label from './task-steps-label.svelte';
import List from './task-steps-list.svelte';
import Meta from './task-steps-meta.svelte';
import Summary from './task-steps-summary.svelte';

export type TaskStep = {
    id: string;
    label: string;
    meta?: string;
};

export type TaskStepStatus = 'pending' | 'active' | 'done' | 'error';

export type TaskStepsLabels = {
    /** Accessible name of the step list. */
    list?: string;
    /** Summary when there are no steps. */
    empty?: string;
    /** Summary while a step runs. `position` starts at 1. */
    progress?: (step: string, position: number, total: number) => string;
    /** Summary once every step is done. */
    complete?: (total: number) => string;
    /** Summary when a step fails. */
    failed?: (step: string) => string;
};

export type TaskStepsProps = {
    /**
     * Overrides the built-in text and accessible names. Every key is optional and English is the
     * fallback.
     */
    labels?: TaskStepsLabels;
    /** Content rendered inside. */
    children?: Snippet<[TaskStepsState]>;
    /** Steps to show, in order. */
    steps: TaskStep[];
    /** Index of the step in progress. */
    current: number;
    /** Marks the current step as failed. */
    failed?: boolean;
    /** Classes added to the element. */
    class?: string;
} & Omit<HTMLAttributes<HTMLDivElement>, 'children'>;

export type TaskStepsState = {
    rows: (TaskStep & { status: TaskStepStatus })[];
    current: number;
    completed: number;
    total: number;
    failed: boolean;
    complete: boolean;
    sentence: string;
};
export type TaskStepsListProps = HTMLAttributes<HTMLOListElement>;
export type TaskStepsItemProps = HTMLAttributes<HTMLLIElement> & {
    /** State of the step. Indicator, Label, and Meta read it. */
    status?: TaskStepStatus;
};
export type TaskStepsIndicatorProps = Omit<HTMLAttributes<HTMLSpanElement>, 'children'> & {
    /** State of the step. */
    status?: TaskStepStatus;
};
export type TaskStepsLabelProps = HTMLAttributes<HTMLSpanElement>;
export type TaskStepsMetaProps = HTMLAttributes<HTMLSpanElement>;
export type TaskStepsSummaryProps = Omit<HTMLAttributes<HTMLSpanElement>, 'children'> & {
    /** Content rendered inside. */
    children?: Snippet<[TaskStepsState]>;
};
export { Indicator, Item, Label, List, Meta, Summary, TaskSteps, TaskSteps as Root };
export default TaskSteps;
~~~~

## Examples

### Live

~~~~svelte
<script lang="ts">
    import { type TaskStep, TaskSteps } from '@mielui/svelte/components/task-steps';
    import { onMount } from 'svelte';

    const steps: TaskStep[] = [
        {
            id: 'queue',
            label: 'Queued',
            meta: '0.2s'
        },
        {
            id: 'build',
            label: 'Building',
            meta: '8.1s'
        },
        {
            id: 'checks',
            label: 'Running checks',
            meta: '3.4s'
        },
        {
            id: 'deploy',
            label: 'Deploying',
            meta: '5.0s'
        }
    ];

    let current = $state(0);

    onMount(() => {
        const timer = setInterval(() => {
            current += 1;

            if (current >= steps.length) {
                clearInterval(timer);
            }
        }, 900);

        return () => {
            clearInterval(timer);
        };
    });
</script>

<div class="w-full max-w-sm">
    <TaskSteps {steps} {current} labels={{ list: 'Deploy progress' }} />
</div>
~~~~

### Retry

~~~~svelte
<script lang="ts">
    import { Button } from '@mielui/svelte/components/button';
    import { type TaskStep, TaskSteps } from '@mielui/svelte/components/task-steps';

    const steps: TaskStep[] = [
        {
            id: 'upload',
            label: 'Upload files'
        },
        {
            id: 'validate',
            label: 'Validate records'
        },
        {
            id: 'publish',
            label: 'Publish report'
        }
    ];

    let current = $state(1);
    let failed = $state(true);

    const complete = $derived(current === steps.length);
    const message = $derived.by(() => {
        if (failed) {
            return 'Validation failed. Correct the source file, then retry this step.';
        }

        return complete ? 'Report published.' : 'Validation can continue.';
    });

    function advance() {
        if (failed) {
            failed = false;
        } else {
            current = Math.min(current + 1, steps.length);
        }
    }

    function reset() {
        current = 1;
        failed = true;
    }
</script>

<div class="w-full max-w-sm space-y-4">
    <TaskSteps {steps} {current} {failed} labels={{ list: 'Report import' }} />
    <p role="status" class="text-sm text-foreground-muted">{message}</p>
    <div class="flex gap-2">
        <Button onclick={advance} disabled={complete}>
            {failed ? 'Retry validation' : 'Complete step'}
        </Button>
        <Button variant="secondary" onclick={reset}>Reset</Button>
    </div>
</div>
~~~~

For the rendered reference, visit [/docs/components/task-steps](/docs/components/task-steps).
