Skip to content

Commit

Permalink
Merge pull request #90 from alexzhang1030/feat/vue-binding
Browse files Browse the repository at this point in the history
feat: add Vue binding of Stepperize
  • Loading branch information
damianricobelli authored Jan 9, 2025
2 parents 98c1eb1 + f070cd9 commit 7d8b02e
Show file tree
Hide file tree
Showing 17 changed files with 1,049 additions and 18 deletions.
5 changes: 5 additions & 0 deletions .changeset/old-waves-protect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@stepperize/vue": major
---

feat: add vue binding of Stepperize
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Build @stepperize/react
run: pnpm turbo run build --filter=@stepperize/react
- name: Build stepperize packages
run: pnpm turbo run build --filter=@stepperize/react --filter=@stepperize/vue

- name: Create Release Pull Request or Publish to npm
uses: changesets/action@v1
Expand Down
269 changes: 269 additions & 0 deletions apps/docs/content/docs/vue/api-references/composable.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
---
title: Composable
description: Access and control your stepper with the useStepper composable
---

The `useStepper` composable provides methods to interact with and render your stepper. It can be used with or without the [`Scoped`](./scoped) component.

## Usage

```vue
<script setup lang="ts">
import { defineStepper } from "@stepperize/vue"
const { useStepper } = defineStepper(
{ id: "first" },
{ id: "second" },
{ id: "last" },
)
const stepper = useStepper()
</script>
```

## Rendering Methods

### when

The when method allows rendering content conditionally based on the current step.
It can take an id of a step (either a string or an array of a step ID followed by booleans)
and a whenFn (the function to execute if the step matches). Additionally, you can provide
an optional elseFn (the function to execute if the step does not match).

<Tabs groupId="components" items={['SFC', 'JSX']} defaultValue="sfc">
<Tab value="sfc">
```vue
<script setup lang="ts">
import { defineStepper, StepperWhen } from '@stepperize/vue'
const stepper = useStepper()
</script>
<template>
<StepperWhen :stepper when="first">
<template #default="step">
<p>First step: {{ step.title }}</p>
</template>
</StepperWhen>
<StepperWhen :stepper when="second">
<template #default="step">
<p>Second step: {{ step.title }}</p>
</template>
</StepperWhen>
<StepperWhen :stepper when="last">
<template #default>
Finished!
</template>
<template #else>
Not finished yet
</template>
</StepperWhen>
</template>
```
</Tab>
<Tab value="jsx">
```tsx
import { defineComponent } from 'vue'

const MyStepperComponent = defineComponent(() => {
const stepper = useStepper()

return () => (
<>
{ stepper.value.when("first", step => <p>First step: {step.title}</p>) }
{ stepper.value.when("second", step => <p>Second step: {step.title}</p>) }
{ stepper.value.when("last", "Finished!", "Not finished yet") }
</>
)
})
```
</Tab>
</Tabs>

You can define more complex conditions that not only depend on the current step's ID
but also on additional boolean values. This allows for multi-condition logic where each
boolean must evaluate to true for the step to match. The boolean values can represent
different state conditions or external factors that affect the step's visibility or behavior.

> The first element of the array is the step ID, the following elements are the boolean values.
<Tabs groupId="components" items={['SFC', 'JSX']} defaultValue="sfc">
<Tab value="sfc">
```vue
<script setup lang="ts">
import { defineStepper, StepperWhen } from '@stepperize/vue'
const stepper = useStepper()
const condition1 = ... // boolean
const condition2 = ... // boolean
</script>
<template>
<StepperWhen :stepper :when="['first', condition1, condition2]">
<template #default="step">
<p>First step: {{ step.title }}</p>
</template>
</StepperWhen>
</template>
```
</Tab>
<Tab value="jsx">
```tsx
import { defineComponent } from 'vue'

const MyStepperComponent = defineComponent(() => {
const stepper = useStepper()

return () => (
<>
{ stepper.when(["first", condition1, condition2], step => <p>First step: {step.title}</p>) }
</>
)
})
```
</Tab>
</Tabs>

### switch

The switch method allows you to render content based on the current step's ID,
similar to a switch-case structure. This method provides a cleaner and more scalable
way to handle different step-specific rendering logic, making it ideal for scenarios
where you need to differentiate the UI depending on the current step without writing
multiple when conditions.

<Tabs groupId="components" items={['SFC', 'JSX']} defaultValue="sfc">
<Tab value="sfc">
```vue
<script setup lang="ts">
import { defineStepper, StepperSwitch } from '@stepperize/vue'
const stepper = useStepper()
</script>
<template>
<StepperSwitch :stepper>
<template #first="step">
<p>First: {{ step.title }}</p>
</template>
<template #second="step">
<p>Second: {{ step.title }}</p>
</template>
<template #last>
Finished!
</template>
</StepperSwitch>
</template>
```
</Tab>
<Tab value="jsx">
```tsx
import { defineComponent } from 'vue'

const MyStepperComponent = defineComponent(() => {
const stepper = useStepper()

return () => (
<>
{ stepper.value.switch({
first: step => <p>First: {step.title}</p>,
second: step => <p>Second: {step.title}</p>,
last: () => <p>Finished!</p>,
}) }
</>
)
})
```
</Tab>
</Tabs>

### match

The match method allows you to render content based on an external state,
such as a value fetched from a server or any dynamic state in your application.
This provides flexibility for rendering content that is tied not only to the
current step in the stepper but also to any other state outside the stepper's
context, such as user actions, data from an API, or global application state.

<Tabs groupId="components" items={['SFC', 'JSX']} defaultValue="sfc">
<Tab value="sfc">
```vue
<script setup lang="ts">
import { defineStepper, StepperMatch } from '@stepperize/vue'
const stepper = useStepper()
const state = "Value from server or client state"
</script>
<template>
<StepperMatch :stepper :state>
<template #first="step">
<p>First: {{ step.title }}</p>
</template>
<template #second="step">
<p>Second: {{ step.title }}</p>
</template>
<template #last>
Finished!
</template>
</StepperMatch>
</template>
```
</Tab>
<Tab value="jsx">
```tsx
import { defineComponent } from 'vue'

const MyStepperComponent = defineComponent(() => {
const stepper = useStepper()
const state = "Value from server or client state"

return () => (
<>
{ stepper.value.match(state, {
first: step => <p>First: {step.title}</p>,
second: step => <p>Second: {step.title}</p>,
last: () => <p>Finished!</p>,
}) }
</>
)
})
```
</Tab>
</Tabs>

<Callout type="info">
`match` allows state-based control from client or server, useful for frameworks like Nuxt with server-side state management.
</Callout>

## API Reference

| Name | Type | Description |
| --- | --- | --- |
| all | `Step[]` | Returns all steps |
| current | `Step` | Returns the current step |
| isLast | `boolean` | Returns true if the current step is the last step |
| isFirst | `boolean` | Returns true if the current step is the first step |
| next | `() => void` | Advances to the next step |
| prev | `() => void` | Returns to the previous step |
| get | `(id: string) => Step` | Returns a step by its ID |
| goTo | `(id: string) => void` | Navigates to a specific step by its ID |
| reset | `() => void` | Resets the stepper to its initial state |
| when | `(id: string, whenFn: (step: Step) => VNodeChild, elseFn?: (step: Step) => VNodeChild) => VNodeChild` | Executes a function based on the current step ID |
| switch | `(steps: { [id: string]: (step: Step) => VNodeChild }) => VNodeChild` | Executes a function based on a switch-case-like structure for steps |
| match | `(state: string, steps: { [id: string]: (step: Step) => VNodeChild }) => VNodeChild` | Matches the current state with a set of possible states and executes the corresponding function |
49 changes: 49 additions & 0 deletions apps/docs/content/docs/vue/api-references/define.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
---
title: Define
description: How to define a stepper using defineStepper
---

`defineStepper` is the core function for creating a customizable stepper. It generates the necessary components and composables for building your stepper UI and managing its state.

## Usage

<Steps>
<Step>
### Import

```tsx
import { defineStepper } from "@stepperize/vue";
```
</Step>
<Step>
### Define steps

Call `defineStepper` with your step configurations:

```tsx
const { Scoped, useStepper, steps } = defineStepper(
{ id: "step-1", title: "Step 1", description: "First step" },
{ id: "step-2", title: "Step 2", description: "Second step" },
{ id: "step-3", title: "Step 3", description: "Third step" }
);
```

- `id` (required): Unique identifier for each step
- Add any custom properties (e.g., `title`, `description`) for use in your UI
</Step>
</Steps>

## Return Value

`defineStepper` returns an object with:

- `Scoped`: A Provider component for scoping stepper state
- `useStepper`: A custom composable for accessing and controlling the stepper
- `steps`: An array of the defined step objects

## Next Steps

Learn how to use the returned components and composables:

- [useStepper Composable](./composable)
- [Scoped Component](./scoped)
Loading

0 comments on commit 7d8b02e

Please sign in to comment.