-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #148 from kosori/feat/use-step-hook
- Loading branch information
Showing
5 changed files
with
259 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
--- | ||
title: useStep | ||
description: Copy text to the clipboard | ||
--- | ||
|
||
<ComponentPreview name='use-step'> | ||
```json doc-gen:file | ||
{ | ||
"file": "./src/components/demos/UseStep/UseStep.tsx", | ||
"codeblock": true | ||
} | ||
``` | ||
</ComponentPreview> | ||
|
||
## Installation | ||
|
||
<Steps> | ||
|
||
<Step> | ||
### Copy-paste the hook | ||
|
||
Copy and paste the hook code in a `.ts` file. | ||
|
||
```json doc-gen:file | ||
{ | ||
"file": "./src/hooks/use-step.ts", | ||
"codeblock": true | ||
} | ||
``` | ||
|
||
</Step> | ||
|
||
</Steps> | ||
|
||
### Usage | ||
|
||
```ts | ||
import { useStep } from '~/hooks/use-step'; | ||
``` | ||
|
||
```tsx | ||
const { currentStep, setStep } = useStep(4); | ||
|
||
<p>{currentStep}</p>; | ||
``` | ||
|
||
## Details | ||
|
||
### Step Navigation | ||
|
||
- The `useStep` hook provides a straightforward way to manage multi-step navigation in applications, such as wizards or forms. It allows users to move between steps easily while enforcing boundaries based on the maximum step defined. | ||
- The hook ensures that users cannot navigate beyond the defined steps, preventing errors and enhancing user experience. | ||
|
||
### State Management | ||
|
||
- **`currentStep`**: A state variable that holds the current step number, initialized to `1`. This allows components to easily access the current step and render appropriate content based on it. | ||
- **`canGoToNextStep`**: A boolean that indicates whether the user can proceed to the next step. This is useful for enabling or disabling navigation buttons. | ||
- **`canGoToPrevStep`**: A boolean that indicates whether the user can go back to the previous step, providing similar functionality for backward navigation. | ||
|
||
### Step Control Functions | ||
|
||
- **`goToNextStep`**: A function that increments the `currentStep` by `1` if the next step is available. This function can be called directly from UI elements like buttons. | ||
- **`goToPrevStep`**: A function that decrements the `currentStep` by `1` if the previous step exists, allowing users to navigate backward through the steps. | ||
- **`setStep`**: A versatile function that allows setting the current step either directly with a number or by providing a function that computes the new step based on the previous one. This flexibility mimics the API of `useState`. | ||
|
||
### Reset Functionality | ||
|
||
- **`reset`**: A function that resets the `currentStep` back to `1`. This is particularly useful for scenarios where the user might want to restart the process or when the form is submitted successfully. | ||
|
||
### Return Value | ||
|
||
- The hook returns an object containing: | ||
- **`currentStep`**: The current step number, which can be used to render step-specific content. | ||
- **`goToNextStep`**: A function to navigate to the next step. | ||
- **`goToPrevStep`**: A function to navigate to the previous step. | ||
- **`canGoToNextStep`**: A boolean indicating if the next step is available. | ||
- **`canGoToPrevStep`**: A boolean indicating if the previous step is available. | ||
- **`setStep`**: A function to set the current step directly or via a function. | ||
- **`reset`**: A function to reset the step to the initial state. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
'use client'; | ||
|
||
import { | ||
ChevronLeftIcon, | ||
ChevronRightIcon, | ||
DoubleArrowLeftIcon, | ||
DoubleArrowRightIcon, | ||
} from '@radix-ui/react-icons'; | ||
|
||
import { Button } from '@kosori/ui/button'; | ||
|
||
import { useStep } from '~/hooks/use-step'; | ||
|
||
export const UseStepDemo = () => { | ||
const { | ||
currentStep, | ||
canGoToPrevStep, | ||
canGoToNextStep, | ||
setStep, | ||
goToPrevStep, | ||
goToNextStep, | ||
} = useStep(4); | ||
|
||
return ( | ||
<div className='flex flex-col items-center justify-center gap-4'> | ||
<p className='font-mono'>Current step: {currentStep}</p> | ||
|
||
<div className='flex gap-1'> | ||
<Button | ||
disabled={!canGoToPrevStep} | ||
icon | ||
variant='ghost' | ||
onClick={() => setStep(1)} | ||
> | ||
<DoubleArrowLeftIcon /> | ||
</Button> | ||
|
||
<Button | ||
disabled={!canGoToPrevStep} | ||
icon | ||
variant='ghost' | ||
onClick={goToPrevStep} | ||
> | ||
<ChevronLeftIcon /> | ||
</Button> | ||
|
||
<Button | ||
icon | ||
variant={currentStep === 1 ? 'outline' : 'ghost'} | ||
onClick={() => setStep(1)} | ||
> | ||
1 | ||
</Button> | ||
|
||
<Button | ||
icon | ||
variant={currentStep === 2 ? 'outline' : 'ghost'} | ||
onClick={() => setStep(2)} | ||
> | ||
2 | ||
</Button> | ||
|
||
<Button | ||
icon | ||
variant={currentStep === 3 ? 'outline' : 'ghost'} | ||
onClick={() => setStep(3)} | ||
> | ||
3 | ||
</Button> | ||
|
||
<Button | ||
icon | ||
variant={currentStep === 4 ? 'outline' : 'ghost'} | ||
onClick={() => setStep(4)} | ||
> | ||
4 | ||
</Button> | ||
|
||
<Button | ||
disabled={!canGoToNextStep} | ||
onClick={goToNextStep} | ||
icon | ||
variant='ghost' | ||
> | ||
<ChevronRightIcon /> | ||
</Button> | ||
|
||
<Button | ||
disabled={!canGoToNextStep} | ||
onClick={() => setStep(4)} | ||
icon | ||
variant='ghost' | ||
> | ||
<DoubleArrowRightIcon /> | ||
</Button> | ||
</div> | ||
</div> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './UseStep'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import { useCallback, useState } from 'react'; | ||
|
||
/** | ||
* A custom hook that manages multi-step navigation in applications. | ||
* | ||
* @param {number} [maxStep] - The maximum number of steps in the navigation process. | ||
* | ||
* @returns An object containing the following properties: | ||
* - `currentStep`: The current step number, initialized to 1. | ||
* - `goToNextStep`: A function that increments the current step by 1 if the next step is available. | ||
* - `goToPrevStep`: A function that decrements the current step by 1 if the previous step exists. | ||
* - `canGoToNextStep`: A boolean indicating whether the user can proceed to the next step. | ||
* - `canGoToPrevStep`: A boolean indicating whether the user can go back to the previous step. | ||
* - `setStep`: A function that sets the current step either directly with a number or via a function that computes the new step based on the previous one. | ||
* - `reset`: A function that resets the current step back to 1. | ||
* | ||
* @example | ||
* const { currentStep, goToNextStep, setStep, reset } = useStep(5); | ||
* | ||
* <p>{currentStep}</p> | ||
* <button onClick={goToNextStep}>Next</button> | ||
* <button onClick={() => setStep(currentStep + 2)}>Set Step to 3</button> | ||
* <button onClick={reset}>Reset</button> | ||
*/ | ||
export const useStep = (maxStep: number) => { | ||
const [currentStep, setCurrentStep] = useState(1); | ||
|
||
const canGoToNextStep = currentStep + 1 <= maxStep; | ||
const canGoToPrevStep = currentStep - 1 > 0; | ||
|
||
const setStep = useCallback( | ||
(step: number | ((step: number) => number)) => { | ||
// Allow value to be a function so we have the same API as useState | ||
const newStep = step instanceof Function ? step(currentStep) : step; | ||
|
||
if (newStep >= 1 && newStep <= maxStep) { | ||
setCurrentStep(newStep); | ||
return; | ||
} | ||
|
||
throw new Error('Step not valid'); | ||
}, | ||
[maxStep, currentStep], | ||
); | ||
|
||
const goToNextStep = useCallback(() => { | ||
if (canGoToNextStep) { | ||
setCurrentStep((step) => step + 1); | ||
} | ||
}, [canGoToNextStep]); | ||
|
||
const goToPrevStep = useCallback(() => { | ||
if (canGoToPrevStep) { | ||
setCurrentStep((step) => step - 1); | ||
} | ||
}, [canGoToPrevStep]); | ||
|
||
const reset = useCallback(() => { | ||
setCurrentStep(1); | ||
}, []); | ||
|
||
return { | ||
currentStep, | ||
goToNextStep, | ||
goToPrevStep, | ||
canGoToNextStep, | ||
canGoToPrevStep, | ||
setStep, | ||
reset, | ||
}; | ||
}; |