Skip to content

Commit

Permalink
Merge pull request #109 from alexzhang1030/feat/sync-vue
Browse files Browse the repository at this point in the history
feat: add metadata api to vue binding
  • Loading branch information
damianricobelli authored Feb 13, 2025
2 parents e9e64b2 + ef3593b commit 790e451
Show file tree
Hide file tree
Showing 7 changed files with 216 additions and 59 deletions.
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

0 comments on commit 790e451

Please sign in to comment.