Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add metadata api to vue binding #109

Merged
merged 5 commits into from
Feb 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/strange-singers-fail.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@stepperize/vue": major
---

feat: add metadata api to vue binding
2 changes: 2 additions & 0 deletions apps/docs/content/docs/react/api-references/hook.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,8 @@ stepper.resetMetadata(true | false);
| afterNext | `(callback: () => Promise<void> \| void) => void` | Executes a function after moving to the next step |
| beforePrev | `(callback: () => Promise<boolean> \| boolean) => void` | Executes a function before moving to the previous step |
| afterPrev | `(callback: () => Promise<void> \| void) => void` | Executes a function after moving to the previous step |
| beforeGoTo | `(id: string, callback: () => Promise<boolean> \| boolean) => void` | Executes a function before moving to a specific step |
| afterGoTo | `(id: string, callback: () => Promise<void> \| void) => void` | Executes a function after moving to a specific 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 |
Expand Down
134 changes: 115 additions & 19 deletions apps/docs/content/docs/vue/api-references/composable.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -306,24 +306,120 @@ It returns a promise that resolves to a void value.

In case you don't need a promise, you can use the `afterPrev` returning a void value.

## API Reference
### beforeGoTo

The `beforeGoTo` function allows you to execute a function before moving to a specific step.
It returns a promise that resolves to a boolean value. If the promise resolves to true,
the stepper will move to the specific step. If the promise resolves to false, the stepper
will not move to the specific step.

| 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 |
| beforeNext | `(callback: () => Promise<boolean> \| boolean) => void` | Executes a function before moving to the next step |
| afterNext | `(callback: () => Promise<void> \| void) => void` | Executes a function after moving to the next step |
| beforePrev | `(callback: () => Promise<boolean> \| boolean) => void` | Executes a function before moving to the previous step |
| afterPrev | `(callback: () => Promise<void> \| void) => void` | Executes a function after moving to the previous 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) => React.ReactNode, elseFn?: (step: Step) => React.ReactNode) => React.ReactNode` | Executes a function based on the current step ID |
| switch | `(steps: { [id: string]: (step: Step) => React.ReactNode }) => React.ReactNode` | Executes a function based on a switch-case-like structure for steps |
| match | `(state: string, steps: { [id: string]: (step: Step) => React.ReactNode }) => React.ReactNode` | Matches the current state with a set of possible states and executes the corresponding function |
```vue
<script setup lang="ts">
const stepper = useStepper();
</script>
<template>
<button
@click="() =>
stepper.beforeGoTo('first', () => {
// Your logic here
return true; // or false
})
"
>
Go to first step
</button>
</template>
```

### afterGoTo

The `afterGoTo` function allows you to execute a function after moving to a specific step.
It returns a promise that resolves to a void value.

```vue
<script setup lang="ts">
const stepper = useStepper();
</script>
<template>
<button
@click="() =>
stepper.afterGoTo('first', () => {
// Your logic here
})
"
>
Go to first step
</button>
</template>
```

## Metadata

The metadata is a way to add custom data to a step. It is useful for adding dynamic values to a step, such as a value fetched from a server or any dynamic state in your application.

In order to add metadata to your stepper, you can add initial metadata in the `useStepper` composable.

```tsx
const stepper = useStepper({
initialMetadata: {
first: {
value: "1",
},
},
});
```

This metadata will be available in the `metadata` property of the `useStepper` composable and you can also use methods to set, get and reset the metadata.

### setMetadata

Allows you to set the metadata for a step.

```tsx
stepper.value.setMetadata("first", {
value: "1",
});
```

### getMetadata

The `getMetadata` method allows you to get the metadata for a step.

```tsx
const metadata = stepper.value.getMetadata("first");
```

### resetMetadata

The `resetMetadata` method allows you to reset the metadata for a step. You can also pass a boolean value to keep the initial metadata defined in the `useStepper` composable.

```tsx
stepper.value.resetMetadata(true | false);
```

## 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 |
| metadata | `Record<string, Metadata>` | Returns the metadata for the current step |
| setMetadata | `(id: string, values: Metadata) => void` | Sets the metadata for a step |
| getMetadata | `(id: string) => Metadata` | Returns the metadata for a step |
| resetMetadata | `(keepInitialMetadata?: boolean) => void` | Resets the metadata. If keepInitialMetadata is true, the initial metadata defined in the useStepper composable will be kept. |
| beforeNext | `(callback: () => Promise<boolean> \| boolean) => void` | Executes a function before moving to the next step |
| afterNext | `(callback: () => Promise<void> \| void) => void` | Executes a function after moving to the next step |
| beforePrev | `(callback: () => Promise<boolean> \| boolean) => void` | Executes a function before moving to the previous step |
| afterPrev | `(callback: () => Promise<void> \| void) => void` | Executes a function after moving to the previous step |
| beforeGoTo | `(id: string, callback: () => Promise<boolean> \| boolean) => void` | Executes a function before moving to a specific step |
| afterGoTo | `(id: string, callback: () => Promise<void> \| void) => void` | Executes a function after moving to a specific 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) => React.ReactNode, elseFn?: (step: Step) => React.ReactNode) => React.ReactNode` | Executes a function based on the current step ID |
| switch | `(steps: { [id: string]: (step: Step) => React.ReactNode }) => React.ReactNode` | Executes a function based on a switch-case-like structure for steps |
| match | `(state: string, steps: { [id: string]: (step: Step) => React.ReactNode }) => React.ReactNode` | Matches the current state with a set of possible states and executes the corresponding function |
4 changes: 3 additions & 1 deletion apps/docs/content/docs/vue/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
"api-references/define",
"api-references/composable",
"api-references/scoped",
"api-references/utils"
"api-references/utils",
"---Migration---",
"migration/migrating-to-v2"
]
}
22 changes: 22 additions & 0 deletions apps/docs/content/docs/vue/migration/migrating-to-v2.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
title: Migrating to v2
description: Learn how to migrate from v1 to v2
icon: CircleFadingArrowUp
---

`@stepperize/vue` adds a new `metadata` API to the `useStepper` composable.

## Breaking Changes

### Name changes in the values returned by the composable

Now the parameters of useStepper are not a string indicating the initial step, but rather an object with the following values:

- `initialStep` The ID of the initial step to display
- `initialMetadata` The initial metadata to set for the steps

## New Features

### New `metadata` API

The `metadata` API allows you to set dynamic metadata for each step.
41 changes: 33 additions & 8 deletions packages/vue/src/define-stepper.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import type { Get, Step, Stepper } from "@stepperize/core";
import type { Get, Metadata, Step, Stepper } from "@stepperize/core";
import {
executeStepCallback,
generateCommonStepperUseFns,
generateStepperUtils,
getInitialMetadata,
getInitialStepIndex,
} from "@stepperize/core";
import {
Expand All @@ -24,10 +25,15 @@ export const defineStepper = <const Steps extends Step[]>(...steps: Steps): Step

const utils = generateStepperUtils(...steps);

const useStepper = (initialStep?: MaybeRefOrGetter<Get.Id<Steps>>) => {
const stepIndex = ref(getInitialStepIndex(steps, toValue(initialStep)));
const useStepper = (config?: {
initialStep?: MaybeRefOrGetter<Get.Id<Steps>>;
initialMetadata?: Partial<Record<Get.Id<Steps>, Metadata>>;
}) => {
const stepIndex = ref(getInitialStepIndex(steps, toValue(config?.initialStep)));
const metadata = ref(getInitialMetadata(steps, toValue(config?.initialMetadata)));

watch(
() => toValue(initialStep),
() => toValue(config?.initialStep),
(value) => {
stepIndex.value = getInitialStepIndex(steps, value);
},
Expand All @@ -47,6 +53,17 @@ export const defineStepper = <const Steps extends Step[]>(...steps: Steps): Step
current: currentStep,
isLast,
isFirst,
metadata: metadata.value,
setMetadata(id, data) {
if (metadata.value[id] === data) return
metadata.value[id] = data
},
getMetadata(id) {
return metadata.value[id];
},
resetMetadata(keepInitialMetadata) {
metadata.value = getInitialMetadata(steps, keepInitialMetadata ? config?.initialMetadata : undefined);
},
async beforeNext(callback) {
if (isLast) {
throw new Error("Cannot navigate to the next step because it is the last step.");
Expand Down Expand Up @@ -91,12 +108,20 @@ export const defineStepper = <const Steps extends Step[]>(...steps: Steps): Step
get(id) {
return steps.find((step) => step.id === id);
},
async beforeGoTo(id, callback) {
const shouldProceed = await executeStepCallback(callback, true);
if (shouldProceed) this.goTo(id);
},
async afterGoTo(id, callback) {
this.goTo(id);
await executeStepCallback(callback, false);
},
goTo(id) {
const index = steps.findIndex((s) => s.id === id);
stepIndex.value = index;
},
reset() {
stepIndex.value = getInitialStepIndex(steps, toValue(initialStep));
stepIndex.value = getInitialStepIndex(steps, toValue(config?.initialStep));
},
...generateCommonStepperUseFns(steps, currentStep, currentStepIndex),
} as Stepper<Steps>;
Expand All @@ -109,11 +134,11 @@ export const defineStepper = <const Steps extends Step[]>(...steps: Steps): Step
steps,
utils,
Scoped: defineComponent<ScopedProps<Steps>>((props, { slots }) => {
provide(contextKey, useStepper(props.initialStep));
provide(contextKey, useStepper(props));
return () => slots.default?.();
}),
useStepper(initialStep) {
return inject(contextKey) ?? useStepper(initialStep);
useStepper(props = {}) {
return inject(contextKey) ?? useStepper(props);
},
};
};
Loading