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(useStepper): new function #1754

Merged
merged 14 commits into from Jul 6, 2022
6 changes: 5 additions & 1 deletion packages/contributors.json
Expand Up @@ -166,6 +166,7 @@
"phaust",
"marktnoonan",
"dm4t2",
"melishev",
"mauriciabad",
"mxmvshnvsk",
"AldeonMoriak",
Expand All @@ -185,6 +186,7 @@
"a1xon",
"octref",
"praburangki",
"preeteshjain",
"QiroNT",
"ramonakira",
"Redemption198",
Expand Down Expand Up @@ -221,12 +223,14 @@
"iGalaxyz",
"winter-ice",
"monkeywithacupcake",
"katsuyaU",
"kagurazaka-0",
"koheing",
"kongmoumou",
"laozei6401",
"leovoon",
"likeswinds",
"lxhyl",
"lxnelyclxud",
"lzdFeiFei",
"meteorlxy",
"odex21",
Expand Down
1 change: 1 addition & 0 deletions packages/core/index.ts
Expand Up @@ -94,6 +94,7 @@ export * from './useSessionStorage'
export * from './useShare'
export * from './useSpeechRecognition'
export * from './useSpeechSynthesis'
export * from './useStepper'
export * from './useStorage'
export * from './useStorageAsync'
export * from './useStyleTag'
Expand Down
118 changes: 118 additions & 0 deletions packages/core/useStepper/demo.vue
@@ -0,0 +1,118 @@
<script setup lang="ts">
import { useStepper } from '@vueuse/core'
import { reactive } from 'vue'

const form = reactive({
firstName: 'Jon',
lastName: '',
billingAddress: '',
contractAccepted: false,
carbonOffsetting: false,
payment: 'credit-card' as 'paypal' | 'credit-card',
})

const stepper = useStepper({
'user-information': {
title: 'User information',
isValid: () => form.firstName && form.lastName,
},
'billing-address': {
title: 'Billing address',
isValid: () => form.billingAddress?.trim() !== '',
},
'terms': {
title: 'Terms',
isValid: () => form.contractAccepted === true,
},
'payment': {
title: 'Payment',
isValid: () => ['credit-card', 'paypal'].includes(form.payment),
},
})

function submit() {
if (stepper.current.value.isValid())
stepper.goToNext()
}

function allStepsBeforeAreValid(index: number): boolean {
return !Array(index)
.fill(null)
.some((_, i) => !stepper.at(i)?.isValid())
}
</script>

<template>
<div>
<div class="flex gap-2 justify-center">
<div v-for="(step, id, i) in stepper.steps.value" :key="id" class="">
<button
:disabled="!allStepsBeforeAreValid(i) && stepper.isBefore(id)"
@click="stepper.goTo(id)"
v-text="step.title"
/>
</div>
</div>

<form class="mt-10" @submit.prevent="submit">
<span class="text-lg font-bold" v-text="stepper.current.value.title" />
<div class="flex flex-col justify-center gap-2 mt-2">
<div>
<div v-if="stepper.isCurrent('user-information')">
<span>First name:</span>
<input v-model="form.firstName" class="!mt-0.5" type="text">
<span>Last name:</span>
<input v-model="form.lastName" class="!mt-0.5" type="text">
</div>

<div v-if="stepper.isCurrent('billing-address')">
<input v-model="form.billingAddress" type="text">
</div>

<div v-if="stepper.isCurrent('terms')">
<div>
<input id="carbon-offsetting" v-model="form.carbonOffsetting" type="checkbox" class="mr-2">
<label for="carbon-offsetting">I accept to deposit a carbon offsetting fee</label>
</div>
<div>
<input id="contract" v-model="form.contractAccepted" type="checkbox" class="mr-2">
<label for="contract">I accept the terms of the contract</label>
</div>
</div>

<div v-if="stepper.isCurrent('payment')">
<div>
<input id="credit-card" v-model="form.payment" type="radio" class="mr-2" value="credit-card">
<label for="credit-card">Credit card</label>
</div>
<div>
<input id="paypal" v-model="form.payment" type="radio" class="mr-2" value="paypal">
<label for="paypal">PayPal</label>
</div>
</div>
</div>

<div>
<button v-if="!stepper.isLast" :disabled="!stepper.current.value.isValid()">
Next
</button>
<button v-if="stepper.isLast" :disabled="!stepper.current.value.isValid()">
Submit
</button>
</div>
</div>
</form>

<div class="flex flex-col gap-4 mt-12">
<div class="w-full px-4 py-2 rounded border border-main space-y-2 overflow-auto h-full">
<span class="font-bold">Form</span>
<pre v-text="form" />
</div>

<div class="w-full px-4 py-2 rounded border border-main space-y-2 overflow-auto h-full">
<span class="font-bold">Wizard</span>
<pre v-text="stepper" />
</div>
</div>
</div>
</template>
54 changes: 54 additions & 0 deletions packages/core/useStepper/index.md
@@ -0,0 +1,54 @@
---
category: Utilities
---

# useStepper

Provides helpers for building a multi-step wizard interface.

## Usage

```js
import { useStepper } from '@vueuse/core'

const {
/** List of steps. */
steps,
/** List of step names. */
stepNames,
/** Index of the current step. */
index,
/** Current step. */
current,
/** Next step, or undefined if the current step is the last one. */
next,
/** Previous step, or undefined if the current step is the first one. */
previous,
/** Whether the current step is the first one. */
isFirst,
/** Whether the current step is the last one. */
isLast,
/** Go to the specified step. */
goTo,
/** Go to the next step. Does nothing if the current step is the last one. */
goNext,
/** Go to the previous step. Does nothing if the current step is the previous one. */
goPrevious,
/** Go back to the given step, only if the current step is after. */
goBackTo,
/** Checks whether the given step is the next step. */
isNext,
/** Checks whether the given step is the previous step. */
isPrevious,
/** Checks whether the given step is the current step. */
isCurrent,
/** Checks if the current step is before the given step. */
isBefore,
/** Checks if the current step is after the given step. */
isAfter,
innocenzi marked this conversation as resolved.
Show resolved Hide resolved
} = useStepper([
'billing-address',
'terms',
'payment',
])
```
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also have a short example of object usage?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added it 👍