# 步骤条

## 使用方法

`Stepper` 列出一个流程里的步骤。`activeStep` 是从 0 开始的当前步。它前面的步骤已完成，当前步进行中，后面的还没开始。

步骤可以不按顺序点时，传入 `@step-click`。回调拿到从 0 开始的序号，只有这时步骤才会渲染成按钮。默认的 `separated` 在水平步骤上方画轨道，圆标和标签排在同一行。`indicatorPosition="on-track"` 把圆标嵌进连接线，标签居中放在圆标下面。`indicator` 默认是 `auto`：完成的步骤是勾，当前步是环，后面的步骤是数字。

`horizontalOptions.minimumStepWidth` 默认是 `112`。水平步骤条窄于「112 × 步骤数」时会自动收起，只留当前步，并带 Previous 和 Next。轨道还在。`collapsedVariant` 默认是 `withLabelAndControls`，并跳过禁用的步骤。`withLabel` 不显示这两个按钮，`hiddenLabel` 只留轨道。

```vue
<script setup lang="ts">
import { Step, Stepper } from '@astryx-vue/core/Stepper'
import { ref } from 'vue'

const active = ref(2)
</script>

<template>
  <Stepper
    :active-step="active"
    :horizontal-options="{ minimumStepWidth: 112 }"
    @step-click="active = $event"
  >
    <Step label="Cart" :step="0" />
    <Step label="Shipping" :step="1" />
    <Step label="Payment" :step="2" />
    <Step label="Review" :step="3" />
    <Step label="Confirm" :step="4" />
  </Stepper>
</template>
```

### 结构

- **步骤条**: 装着各步的有序列表。整条流程的方向，以及指示放在哪里，都由它决定。
- **框架**: 把有序的步骤和窄宽度下可选的紧凑摘要组合在一起的布局框。
- **紧凑摘要**: 横向 Stepper 窄到没法给每一步都标字时，直接加在轨道下方的可选一行。horizontalOptions.collapsedVariant 决定这一行是「标签加上一页和下一页」、只有标签，还是不出现、只留一条进度轨。轨道上的布局把指示留在轨上，不会在标签旁边再重复当前指示。无论多窄，每一步的名字都留在无障碍序列里。
- **步骤**: 流程里的一步，也是带着这一步状态的元素。里面有指示、标签、说明，以及属于它的轨段。
- **进度条**: 每一步一条 4px 的分段条。已完成和当前步是填满的。前进一步时，填充沿着刚走过的那段轨长出来，读起来是在前进，而不是一根条在变色。其余变化一次完成：往回走、一次跳过不止一步、中途挂载，以及 prefers-reduced-motion 下的任何变化。一段跨度由不止一段画出时（轨道上的布局会把它分给两步，中间再插内容插槽时是三步），这些段按轨道顺序、以同一个恒定速度走，所以填充读起来是一条线在长，而不是一段段依次点亮。
- **连接线**: 轨道上的布局里，指示之间画出的轨。每条连接线先画一条未填充的线，再在上面画表示已完成进度的强调填充。一条连接线由几段拼成，是布局的实现细节，不是可以单独换主题的部分。用 --step-connector-gap 让轨和指示保持距离。
- **指示**: 数字徽章、勾，或任意自定义图标。由 indicator 属性控制。
- **标签**: 标识这一步的文字。
- **说明**: 标签下面的补充文字。

## 示例

### 自定义内容

垂直步骤各自占页面的一块。内容可以是表单、摘要或横幅，并且只渲染当前步骤。

```vue
<script setup lang="ts">
import { Badge } from '@astryx-vue/core/Badge'
import { Banner } from '@astryx-vue/core/Banner'
import { Button } from '@astryx-vue/core/Button'
import { Card } from '@astryx-vue/core/Card'
import { HStack, VStack } from '@astryx-vue/core/Stack'
import { Step, Stepper } from '@astryx-vue/core/Stepper'
import { Text } from '@astryx-vue/core/Text'
import { TextInput } from '@astryx-vue/core/TextInput'
import { ref } from 'vue'

const active = ref(0)
const name = ref('')
const repository = ref('')
</script>

<template>
  <div style="width: 680px; max-width: 100%">
    <Stepper :active-step="active" orientation="vertical" @step-click="active = $event">
      <Step
        description="Name it and point us at the source"
        indicator="number"
        label="Project details"
        :step="0"
      >
        <VStack v-if="active === 0" :gap="3">
          <HStack :gap="3">
            <TextInput v-model="name" label="Project name" placeholder="My awesome project" width="100%" />
            <TextInput v-model="repository" label="Repository URL" placeholder="https://github.com/..." width="100%" />
          </HStack>
          <div>
            <Button label="Continue" variant="primary" @click="active = 1" />
          </div>
        </VStack>
      </Step>
      <Step
        description="What we detected from your default branch"
        indicator="number"
        label="Review the build"
        :step="1"
      >
        <VStack v-if="active === 1" :gap="3">
          <Card :padding="3" variant="muted">
            <VStack :gap="2">
              <HStack align="center" :gap="2">
                <Text type="label">
                  Next.js 15
                </Text>
                <Badge variant="success">
                  Detected
                </Badge>
              </HStack>
              <Text type="supporting">
                Build command <code>next build</code> · Output <code>.next</code> · Node 20
              </Text>
            </VStack>
          </Card>
          <HStack :gap="2">
            <Button label="Back" variant="secondary" @click="active = 0" />
            <Button label="Looks right" variant="primary" @click="active = 2" />
          </HStack>
        </VStack>
      </Step>
      <Step description="Ships to production" indicator="number" label="Deploy" :step="2">
        <VStack v-if="active === 2" :gap="3">
          <Banner
            description="Later deploys reuse the build cache and finish in under a minute."
            status="info"
            title="First deploy takes a few minutes"
          />
          <HStack :gap="2">
            <Button label="Back" variant="secondary" @click="active = 1" />
            <Button label="Deploy now" variant="primary" @click="active = 3" />
          </HStack>
        </VStack>
      </Step>
      <Step indicator="number" label="Live" :step="3" />
    </Stepper>
  </div>
</template>
```

### 指示器模式

none 只靠轨道和标签表示进度。auto 用勾、环或数字。number 始终显示序号。也可以换成自定义图标。

```vue
<script setup lang="ts">
import { Step, Stepper } from '@astryx-vue/core/Stepper'
import { Text } from '@astryx-vue/core/Text'
import { ref } from 'vue'

const active = ref(2)
</script>

<template>
  <div class="stepper-modes">
    <div>
      <Text type="label">
        None
      </Text>
      <Stepper :active-step="active" orientation="vertical" @step-click="active = $event">
        <Step indicator="none" label="Account" :step="0" />
        <Step indicator="none" label="Profile" :step="1" />
        <Step indicator="none" label="Settings" :step="2" />
        <Step indicator="none" label="Review" :step="3" />
      </Stepper>
    </div>
    <div>
      <Text type="label">
        Auto
      </Text>
      <Stepper :active-step="active" orientation="vertical" @step-click="active = $event">
        <Step label="Account" :step="0" />
        <Step label="Profile" :step="1" />
        <Step label="Settings" :step="2" />
        <Step label="Review" :step="3" />
      </Stepper>
    </div>
    <div>
      <Text type="label">
        Number
      </Text>
      <Stepper :active-step="active" orientation="vertical" @step-click="active = $event">
        <Step indicator="number" label="Account" :step="0" />
        <Step indicator="number" label="Profile" :step="1" />
        <Step indicator="number" label="Settings" :step="2" />
        <Step indicator="number" label="Review" :step="3" />
      </Stepper>
    </div>
    <div>
      <Text type="label">
        Custom icon
      </Text>
      <Stepper :active-step="active" orientation="vertical" @step-click="active = $event">
        <Step label="Account" :step="0">
          <template #icon>
            <span class="css-icon css-icon--sm i-carbon-information" aria-hidden="true" />
          </template>
        </Step>
        <Step label="Profile" :step="1">
          <template #icon>
            <span class="css-icon css-icon--sm i-carbon-search" aria-hidden="true" />
          </template>
        </Step>
        <Step label="Settings" :step="2">
          <template #icon>
            <span class="css-icon css-icon--sm i-carbon-tools" aria-hidden="true" />
          </template>
        </Step>
        <Step label="Review" :step="3">
          <template #icon>
            <span class="css-icon css-icon--sm i-carbon-checkmark" aria-hidden="true" />
          </template>
        </Step>
      </Stepper>
    </div>
  </div>
</template>

<style scoped>
.stepper-modes {
  display: flex;
  flex-wrap: wrap;
  gap: 48px;
}

.stepper-modes > div {
  max-width: 220px;
}
</style>
```

### 轨道上的水平步骤

水平轨道上，指示器嵌进连接线，数字像一串珠子。标签居中放在节点下面。

```vue
<script setup lang="ts">
import { Step, Stepper } from '@astryx-vue/core/Stepper'
import { ref } from 'vue'

const active = ref(2)
</script>

<template>
  <div style="width: 100%; max-width: 640px">
    <Stepper
      :active-step="active"
      indicator-position="on-track"
      @step-click="active = $event"
    >
      <Step indicator="number" label="Workspace" :step="0" />
      <Step indicator="number" label="Team" :step="1" />
      <Step indicator="number" label="Integrations" :step="2" />
      <Step indicator="number" label="Import" :step="3" />
      <Step indicator="number" label="Launch" :step="4" />
    </Stepper>
  </div>
</template>
```

### 轨道上的垂直步骤

垂直轨道上，指示器落在连续的连接线上，标签和说明放在节点旁边。

```vue
<script setup lang="ts">
import { Step, Stepper } from '@astryx-vue/core/Stepper'
import { ref } from 'vue'

const active = ref(2)
</script>

<template>
  <div style="width: 100%; max-width: 400px">
    <Stepper
      :active-step="active"
      indicator-position="on-track"
      orientation="vertical"
      @step-click="active = $event"
    >
      <Step description="Name and configure your workspace" label="Create workspace" :step="0" />
      <Step description="Add collaborators by email" label="Invite team members" :step="1" />
      <Step description="Connect Slack, GitHub, Jira" label="Set up integrations" :step="2" />
      <Step description="Bring in existing projects" label="Import data" :step="3" />
      <Step description="Go live with your team" label="Launch" :step="4" />
    </Stepper>
  </div>
</template>
```

### 校验状态

校验流程里，成功、错误和强调色只改变指示器。连接线的颜色不受状态影响。

```vue
<script setup lang="ts">
import { Step, Stepper } from '@astryx-vue/core/Stepper'
import { ref } from 'vue'

const active = ref(3)
</script>

<template>
  <div style="width: 100%; max-width: 420px">
    <Stepper :active-step="active" orientation="vertical" @step-click="active = $event">
      <Step description="you@example.com" label="Email verified" :step="0" status="success" />
      <Step description="+1 (555) 012-3456" label="Phone verified" :step="1" status="success" />
      <Step description="Passport upload failed" label="Identity document" :step="2" status="error" />
      <Step description="Pending review" label="Address verification" :step="3" status="accent" />
      <Step description="Skipped" is-optional label="Background check" :step="4" />
      <Step label="Account activated" :step="5" />
    </Stepper>
  </div>
</template>
```

### 窄水平收起

宽度 320px 时，四步低于 112px 的最小宽度，收成当前步骤，并带上一步和下一步。

```vue
<script setup lang="ts">
import { Step, Stepper } from '@astryx-vue/core/Stepper'
import { ref } from 'vue'

const active = ref(1)
</script>

<template>
  <div style="width: 320px; max-width: 100%">
    <Stepper
      :active-step="active"
      :horizontal-options="{ minimumStepWidth: 112 }"
      @step-click="active = $event"
    >
      <Step label="Cart" :step="0" />
      <Step description="Where it goes" label="Shipping" :step="1" />
      <Step label="Delivery" :step="2" />
      <Step label="Payment" :step="3" />
    </Stepper>
  </div>
</template>
```

## Stepper 属性

| 名称 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `activeStep` | `number` | - | 当前步骤，从 0 开始。 |
| `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | 排列方向。 |
| `onStepClick` | `(index: number) => void` | - | 传入后步骤变成按钮。`@step-click` 填的就是这个属性。 |
| `label` | `string` | 进度 | 列表的无障碍名称。 |
| `density` | `'compact' \| 'balanced' \| 'spacious'` | `'balanced'` | 每一步周围的间距。 |
| `indicatorPosition` | `'separated' \| 'on-track'` | `'separated'` | 标记落在连接线上，还是和一条单独的进度条分开。 |
| `hasCollapsibleLabels` | `boolean` | `false` | 水平步骤条窄于 480px 时，只留下当前步骤的文字。 |
| `horizontalOptions` | `{ minimumStepWidth?: number, collapsedVariant?: 'withLabelAndControls' \| 'withLabel' \| 'hiddenLabel' }` | `{ minimumStepWidth: 112, collapsedVariant: 'withLabelAndControls' }` | 水平步骤条窄于 `minimumStepWidth` 乘以步骤数时保留轨道，并显示当前步。`withLabelAndControls` 在设置了 `onStepClick` 时带上 Previous / Next。`withLabel` 只显示标签。`hiddenLabel` 只留轨道。 |

## Step 属性

| 名称 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `step` | `number` | - | 这一步的序号，从 0 开始。 |
| `label` | `string` | - | 步骤名称。 |
| `description` | `string` | - | 名称下面的说明。 |
| `status` | `'accent' \| 'success' \| 'warning' \| 'error'` | - | 只给指示器换色。连接线仍按进度着色。 |
| `isDisabled` | `boolean` | `false` | 这一步不能点。 |
| `isOptional` | `boolean` | `false` | 在名称后标出可选。 |
| `indicator` | `'auto' \| 'number' \| 'none'` | `'auto'` | `auto` 显示勾、圆环或数字。`number` 始终显示序号。 |
| `density` | `'compact' \| 'balanced' \| 'spacious'` | 跟随步骤条 | 这一步自己的间距。 |

## Step 插槽

| 名称 | 说明 |
| --- | --- |
| `default` | 名称下面的内容。垂直且进度条分开时，它在按钮外面。 |
| `icon` | 替换指示器里的图形。 |
| `end` | 名称这一行末尾的内容。 |
