UNPKG

preline

Version:

Preline UI is an open-source set of prebuilt UI components based on the utility-first Tailwind CSS framework.

424 lines (331 loc) 15.7 kB
# Stepper Dynamic stepper plugin that guides users through the steps of a task. [![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/stepper) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/stepper.html) ## Contents - [Overview](#overview) - [Installation](#installation) - [Basic usage](#basic-usage) - [Configuration Options](#configuration-options) - [JavaScript API](#javascript-api) - [Events](#events) - [Common Patterns](#common-patterns) - [License](#license) ## Overview The Stepper component provides a multi-step wizard interface that guides users through a sequential process. It supports both linear (sequential) and non-linear (jumpable) modes, with options for optional steps, error states, and completion tracking. **Key Features:** - Multi-step wizard interface - Linear and non-linear navigation modes - Optional steps support - Error and processing states - Completion tracking - Programmatic control via JavaScript API - Event system for step transitions - Accessibility attributes (ARIA) built-in ## Installation To get started, install Stepper plugin via npm, else you can skip this step if you are already using Preline UI as a package. ```bash npm i @preline/stepper ``` ### CSS Use [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file. ```css @import "tailwindcss"; /* @preline/stepper */ @source "../node_modules/@preline/stepper/*.js"; @import "./node_modules/@preline/stepper/variants.css"; @import "./node_modules/@preline/stepper/theme.css"; ``` ### JavaScript Include the JavaScript that powers the interactive elements near the end of your `</body>` tag: ```html <script src="./node_modules/@preline/stepper/index.js"></script> ``` ### Manual Initialization Use the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly. ```html <script type="module"> import HSStepper from "@preline/stepper/non-auto.mjs"; new HSStepper(document.querySelector("#stepper")); </script> ``` ### Via Bundler When using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module. `@preline/stepper` is the auto-init entry: it scans the DOM and initializes matching elements automatically. ```js import "@preline/stepper"; ``` `@preline/stepper/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself. ```js import HSStepper from "@preline/stepper/non-auto"; HSStepper.autoInit(); // Or initialize a specific element manually const el = document.querySelector("#stepper"); if (el) new HSStepper(el); ``` ### TypeScript This package ships with TypeScript type definitions. No additional `@types/` package is needed. ## Basic usage The following example demonstrates the minimal HTML structure required for a stepper component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The example shows three steps with navigation buttons. ```html <div data-hs-stepper> <div class="hs-stepper-active:text-blue-500 hs-stepper-success:text-blue-500" data-hs-stepper-nav-item='{ "index": 1 }'> 1 Step </div> <div class="hs-stepper-active:text-blue-500 hs-stepper-success:text-blue-500" data-hs-stepper-nav-item='{ "index": 2 }'> 2 Step </div> <div class="hs-stepper-active:text-blue-500 hs-stepper-success:text-blue-500" data-hs-stepper-nav-item='{ "index": 3 }'> 3 Step </div> <div data-hs-stepper-content-item='{ "index": 1 }' style="display: none;"> First content. </div> <div data-hs-stepper-content-item='{ "index": 2 }' style="display: none;"> Second content. </div> <div data-hs-stepper-content-item='{ "index": 3 }' style="display: none;"> Third content. </div> <div data-hs-stepper-content-item='{ "isFinal": true }' style="display: none;"> Final content. </div> <button class="hs-stepper-disabled:opacity-50" type="button" data-hs-stepper-back-btn> Back </button> <button type="button" data-hs-stepper-skip-btn style="display: none;"> Skip </button> <button type="button" data-hs-stepper-next-btn> Next </button> <button type="button" data-hs-stepper-finish-btn style="display: none;"> Finish </button> <button type="reset" data-hs-stepper-reset-btn style="display: none;"> Reset </button> </div> ``` **Structure Requirements:** - `data-hs-stepper`: Required on the container element - `data-hs-stepper-nav-item`: Required on each navigation item - `data-hs-stepper-content-item`: Required on each content panel - `data-hs-stepper-back-btn`: Optional back button - `data-hs-stepper-next-btn`: Optional next button - `data-hs-stepper-finish-btn`: Optional finish button - `data-hs-stepper-skip-btn`: Optional skip button - `data-hs-stepper-reset-btn`: Optional reset button **Initial State:** - First step is active by default (index 1) - Content items should have `style="display: none;"` except the first one - Navigation buttons visibility is controlled by the plugin ## Configuration Options ### Data Options Data options are specified in the `data-hs-stepper`, `data-hs-stepper-nav-item`, and `data-hs-stepper-content-item` attributes. | Attribute | Target Element | Type | Default | Description | | --- | --- | --- | --- | --- | | `data-hs-stepper` | Container | - | - | Activate a Stepper by specifying on an element. Should be added to the container. | | `:currentIndex` | Inside `data-hs-stepper` | number | `1` | Index of the current step. This must be a number between 1 and the maximum number of steps. | | `:mode` | Inside `data-hs-stepper` | `"linear"` \| `"non-linear"` | `"linear"` | The mode of the stepper. In `"non-linear"` mode, the user can navigate to any step and nav items are clickable. In `"linear"` mode, the user can only navigate to the next/back step. | | `:isCompleted` | Inside `data-hs-stepper` | boolean | `false` | Whether the stepper is completed. | | `data-hs-stepper-nav-item` | Navigation item | - | - | Activate a Stepper Nav Item by specifying on an element. Should be added to the nav item. | | `:index` | Inside `data-hs-stepper-nav-item` | number | - | The index of the step to which the item belongs. This must be a number between 1 and the maximum number of steps. | | `:isOptional` | Inside `data-hs-stepper-nav-item` | boolean | `false` | Whether the step is optional. | | `:isCompleted` | Inside `data-hs-stepper-nav-item` | boolean | `false` | Whether the step is completed. | | `:isSkip` | Inside `data-hs-stepper-nav-item` | boolean | `false` | Whether the step is skipped. | | `:hasError` | Inside `data-hs-stepper-nav-item` | boolean | `false` | Whether the step has an error. | | `data-hs-stepper-content-item` | Content item | - | - | Activate a Stepper Content Item by specifying on an element. Should be added to the content item. | | `:index` | Inside `data-hs-stepper-content-item` | number | - | The index of the step to which the item belongs. This must be a number between 1 and the maximum number of steps. | | `:isCompleted` | Inside `data-hs-stepper-content-item` | boolean | `false` | Whether the step is completed. | | `:isSkip` | Inside `data-hs-stepper-content-item` | boolean | `false` | Whether the step is skipped. | | `:isFinal` | Inside `data-hs-stepper-content-item` | boolean | `false` | Whether the step is final. | ### Tailwind Modifiers | Name | Description | | --- | --- | | `hs-stepper-active:*` | Modifies the active step. | | `hs-stepper-success:*` | Modifies the completed step. | | `hs-stepper-disabled:*` | Modifies the "back" button when the very first step is active. | | `hs-stepper-skipped:*` | Modifies the skipped step. | | `hs-stepper-error:*` | Modifies the step that has error. Error class should be added manually by some event. E.g. after form validation. | | `hs-stepper-process:*` | Modifies the step that processing. Process class should be added manually by some event. E.g. after form submit. | | `hs-stepper-completed:*` | Modifies all steps are completed. | ## JavaScript API The `HSStepper` object is available in the global `window` object after the plugin is loaded. ### Instance Methods These methods are called on a stepper instance. | Method | Parameters | Return Type | Description | | --- | --- | --- | --- | | `setProcessedNavItem(n)` | `n`: `number` | `void` | Set the nav item as processed. `n` is the index of the step to which the item belongs. | | `setErrorNavItem(n)` | `n`: `number` | `void` | Set the nav item as error. `n` is the index of the step to which the item belongs. | | `unsetProcessedNavItem(n)` | `n`: `number` | `void` | Unset the nav item as processed. `n` is the index of the step to which the item belongs. | | `goToNext()` | None | `number` | Go to the next step. Returns the index of the next step. If the current step is the last, returns the index of the current step. | | `goToFinish()` | None | `number` | Go to the finish step. Returns the index of the finish step. If the current step is the last, returns the index of the current step. | | `disableButtons()` | None | `void` | Disable the "next" and "back" buttons. | | `enableButtons()` | None | `void` | Enable the "next" and "back" buttons. | | `destroy()` | None | `void` | Destroys the stepper instance, removes all generated markup, classes, and event listeners. Use when removing stepper from DOM. | ### Static Methods These methods are called directly on the `HSStepper` class. | Method | Parameters | Return Type | Description | | --- | --- | --- | --- | | `HSStepper.getInstance(target, isInstance)` | `target`: `HTMLElement \| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HSStepper \| { id: string \| number, element: HSStepper } \| null` | Returns the stepper instance associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSStepper` instance. If `isInstance` is `false` or omitted, returns the `HSStepper` instance directly. Returns `null` if stepper instance is not found. | ### Usage Examples **Example 1: Using instance methods (public API)** ```javascript // Create a new stepper instance const stepper = new HSStepper(document.querySelector('#hs-stepper')); let errorState = 1; stepper.on('beforeNext', (index) => { if (index === 2) { stepper.setProcessedNavItem(index); setTimeout(() => { stepper.unsetProcessedNavItem(index); stepper.enableButtons(); if (errorState) { stepper.goToNext(); } else { stepper.setErrorNavItem(index); } errorState = !errorState; }, 2000); } }); ``` **Example 2: Getting instance and using methods (recommended pattern)** ```javascript // Get the stepper instance const instance = HSStepper.getInstance('#hs-stepper', true); if (instance) { const { element } = instance; // element is the HSStepper instance let errorState = 1; element.on('beforeNext', (index) => { if (index === 2) { element.setProcessedNavItem(index); setTimeout(() => { element.unsetProcessedNavItem(index); element.enableButtons(); if (errorState) { element.goToNext(); } else { element.setErrorNavItem(index); } errorState = !errorState; }, 2000); } }); // Clean up when removing from DOM function removeStepper() { element.destroy(); } } ``` **Example 3: Programmatic navigation** ```javascript const instance = HSStepper.getInstance('#hs-stepper', true); if (instance) { const { element } = instance; // Navigate to next step const nextIndex = element.goToNext(); console.log('Moved to step:', nextIndex); // Navigate to finish const finishIndex = element.goToFinish(); console.log('Finished at step:', finishIndex); } ``` **Example 4: Destroying stepper instance** ```javascript const instance = HSStepper.getInstance('#hs-stepper', true); if (instance) { const { element } = instance; const removeBtn = document.querySelector('#hs-remove-btn'); removeBtn.addEventListener('click', () => { // Clean up before removing from DOM element.destroy(); // Now safe to remove the element document.querySelector('#hs-stepper').remove(); }); } ``` ## Events Stepper instances emit events that can be listened to for step transitions and custom behavior. | Event Name | When Fired | Callback Parameter | Description | | --- | --- | --- | --- | | `on:active` | When the "active" class is set up | `currentIndex` (number) | Fires when a step becomes active. | | `on:beforeNext` | Before the "next" button is clicked | `currentIndex` (number) | Fires before navigating to the next step. Can be used to validate or prevent navigation. | | `on:beforeFinish` | Before the "finish" button is clicked | `currentIndex` (number) | Fires before finishing the stepper. Can be used to validate or prevent completion. | | `on:next` | When the "next" button is clicked | `currentIndex` (number) | Fires when navigating to the next step. | | `on:back` | When the "back" button is clicked | `currentIndex` (number) | Fires when navigating to the previous step. | | `on:complete` | When the "complete" button is clicked | `currentIndex` (number) | Fires when completing a step. | | `on:finish` | When the "finish" button is clicked | `currentIndex` (number) | Fires when finishing the stepper. | ### Event Usage Example ```javascript // Get stepper instance const instance = HSStepper.getInstance('#hs-stepper', true); if (instance) { const { element } = instance; // Listen to step activation element.on('active', (currentIndex) => { console.log('Step activated:', currentIndex); // Perform actions when step becomes active // e.g., load content, initialize form fields }); // Listen to before next (for validation) element.on('beforeNext', (currentIndex) => { console.log('Before next step:', currentIndex); // Validate current step before proceeding // Can prevent navigation if validation fails }); // Listen to finish element.on('finish', (currentIndex) => { console.log('Stepper finished:', currentIndex); // Perform final actions // e.g., submit form, show success message }); } ``` ## Common Patterns ### Pattern 1: Non-linear Mode Allow users to jump to any step. ```html <div data-hs-stepper='{ "mode": "non-linear" }'> <!-- Stepper content --> </div> ``` ### Pattern 2: Error Handling Set error state on a step after validation fails. ```html <div id="hs-stepper-first" data-hs-stepper> <!-- Stepper content --> </div> <script> const instance = HSStepper.getInstance('#hs-stepper-first', true); if (instance) { const { element } = instance; // Validate and set error if needed function validateStep(stepIndex) { // Validation logic if (validationFails) { element.setErrorNavItem(stepIndex); } } } </script> ``` ## License Copyright (c) 2026 Preline Labs. Licensed under the [MIT License](https://opensource.org/licenses/MIT).