├── .npmrc
├── packages
├── svelte
│ ├── src
│ │ └── index.ts
│ ├── tsup.config.ts
│ ├── tsconfig.json
│ ├── package.json
│ └── CHANGELOG.md
├── core
│ ├── src
│ │ └── index.ts
│ ├── tsconfig.json
│ ├── tsup.config.ts
│ ├── package.json
│ └── CHANGELOG.md
├── solid
│ ├── src
│ │ ├── index.ts
│ │ ├── types.ts
│ │ └── define-stepper.ts
│ ├── tsconfig.json
│ ├── tsup.config.ts
│ ├── package.json
│ ├── README.md
│ └── CHANGELOG.md
├── react
│ ├── vite.config.ts
│ ├── src
│ │ ├── index.ts
│ │ ├── types.ts
│ │ └── define-stepper.ts
│ ├── tsconfig.json
│ ├── tsup.config.ts
│ ├── package.json
│ └── README.md
└── vue
│ ├── src
│ ├── index.ts
│ ├── components.ts
│ ├── types.ts
│ └── define-stepper.ts
│ ├── tsconfig.json
│ ├── tsup.config.ts
│ ├── package.json
│ └── README.md
├── apps
└── docs
│ ├── lib
│ ├── cn.ts
│ ├── utils.ts
│ ├── is-active.ts
│ ├── source.ts
│ ├── metadata.ts
│ └── use-copy-button.ts
│ ├── content
│ └── docs
│ │ ├── meta.json
│ │ ├── svelte
│ │ ├── meta.json
│ │ └── index.mdx
│ │ ├── solid
│ │ ├── meta.json
│ │ ├── installation.mdx
│ │ ├── index.mdx
│ │ └── api-references
│ │ │ ├── define.mdx
│ │ │ └── utils.mdx
│ │ ├── vue
│ │ ├── meta.json
│ │ ├── installation.mdx
│ │ ├── migration
│ │ │ └── migrating-to-v2.mdx
│ │ ├── index.mdx
│ │ └── api-references
│ │ │ ├── define.mdx
│ │ │ └── utils.mdx
│ │ └── react
│ │ ├── examples
│ │ ├── conform-react.mdx
│ │ ├── react-hook-form.mdx
│ │ ├── scoped.mdx
│ │ ├── multi-scoped.mdx
│ │ └── basic.mdx
│ │ ├── shadcn
│ │ ├── introduction.mdx
│ │ └── examples.mdx
│ │ ├── migration
│ │ ├── migrating-to-v5.mdx
│ │ ├── migrating-to-v3.mdx
│ │ └── migrating-to-v2.mdx
│ │ ├── installation.mdx
│ │ ├── meta.json
│ │ ├── index.mdx
│ │ └── api-references
│ │ ├── define.mdx
│ │ ├── scoped.mdx
│ │ ├── schema-validation.mdx
│ │ └── utils.mdx
│ ├── app
│ ├── icon.png
│ ├── apple-icon.png
│ ├── og
│ │ └── [...slug]
│ │ │ ├── JetBrainsMono-Bold.ttf
│ │ │ ├── JetBrainsMono-Regular.ttf
│ │ │ ├── route.tsx
│ │ │ └── og.tsx
│ ├── robots.txt
│ ├── api
│ │ └── search
│ │ │ └── route.ts
│ ├── (home)
│ │ ├── layout.tsx
│ │ └── page.tsx
│ ├── sitemap.ts
│ ├── favicon.svg
│ ├── layout.tsx
│ ├── layout.config.tsx
│ └── docs
│ │ ├── layout.tsx
│ │ └── [[...slug]]
│ │ └── page.tsx
│ ├── postcss.config.mjs
│ ├── public
│ └── banner.png
│ ├── cli.json
│ ├── components
│ ├── steps.tsx
│ ├── demo-viewer.tsx
│ ├── ui
│ │ ├── button.tsx
│ │ ├── collapsible.tsx
│ │ ├── popover.tsx
│ │ ├── scroll-area.tsx
│ │ └── tabs.tsx
│ ├── heading.tsx
│ ├── highlighter-code.tsx
│ ├── card.tsx
│ ├── callout.tsx
│ ├── open-in-v0.tsx
│ ├── files.tsx
│ ├── layout
│ │ ├── language-toggle.tsx
│ │ └── root-toggle.tsx
│ ├── home
│ │ └── cta.tsx
│ ├── accordion.tsx
│ └── banner.tsx
│ ├── next.config.mjs
│ ├── registry
│ └── new-york
│ │ ├── blocks
│ │ ├── stepper-demo
│ │ │ ├── page.tsx
│ │ │ └── components
│ │ │ │ └── stepper-demo.tsx
│ │ ├── stepper-with-form
│ │ │ └── page.tsx
│ │ ├── stepper-with-icon
│ │ │ ├── page.tsx
│ │ │ └── components
│ │ │ │ └── stepper-with-icon.tsx
│ │ ├── stepper-with-tracking
│ │ │ ├── page.tsx
│ │ │ └── components
│ │ │ │ └── stepper-with-tracking.tsx
│ │ ├── stepper-with-variants
│ │ │ └── page.tsx
│ │ ├── stepper-with-description
│ │ │ ├── page.tsx
│ │ │ └── components
│ │ │ │ └── stepper-with-description.tsx
│ │ ├── stepper-with-label-orientation
│ │ │ ├── page.tsx
│ │ │ └── components
│ │ │ │ └── stepper-with-label-orientation.tsx
│ │ └── stepper-with-responsive-variant
│ │ │ ├── page.tsx
│ │ │ ├── hooks
│ │ │ └── use-media-query.ts
│ │ │ └── components
│ │ │ └── stepper-with-responsive-variant.tsx
│ │ └── ui
│ │ ├── label.tsx
│ │ ├── input.tsx
│ │ ├── radio-group.tsx
│ │ ├── button.tsx
│ │ └── form.tsx
│ ├── .gitignore
│ ├── components.json
│ ├── source.config.ts
│ ├── README.md
│ ├── tsconfig.json
│ └── package.json
├── .vscode
└── settings.json
├── .github
├── CODEOWNERS
├── ISSUE_TEMPLATE
│ ├── feature_request.md
│ └── bug_report.md
├── pull_request_template.md
└── workflows
│ └── release.yml
├── pnpm-workspace.yaml
├── .changeset
├── config.json
└── README.md
├── turbo.json
├── .gitignore
├── package.json
├── biome.json
├── CONTRIBUTING.md
└── README.md
/.npmrc:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/svelte/src/index.ts:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/apps/docs/lib/cn.ts:
--------------------------------------------------------------------------------
1 | export { twMerge as cn } from 'tailwind-merge';
2 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "typescript.tsdk": "node_modules/typescript/lib"
3 | }
4 |
--------------------------------------------------------------------------------
/packages/core/src/index.ts:
--------------------------------------------------------------------------------
1 | export type * from "./types";
2 | export * from "./utils";
3 |
--------------------------------------------------------------------------------
/apps/docs/content/docs/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "pages": ["react", "solid", "svelte", "vue"]
3 | }
4 |
--------------------------------------------------------------------------------
/packages/solid/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./define-stepper";
2 | export type * from "./types";
3 |
--------------------------------------------------------------------------------
/apps/docs/app/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damianricobelli/stepperize/HEAD/apps/docs/app/icon.png
--------------------------------------------------------------------------------
/apps/docs/app/apple-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damianricobelli/stepperize/HEAD/apps/docs/app/apple-icon.png
--------------------------------------------------------------------------------
/apps/docs/postcss.config.mjs:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | '@tailwindcss/postcss': {},
4 | },
5 | };
6 |
--------------------------------------------------------------------------------
/apps/docs/public/banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damianricobelli/stepperize/HEAD/apps/docs/public/banner.png
--------------------------------------------------------------------------------
/apps/docs/app/og/[...slug]/JetBrainsMono-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damianricobelli/stepperize/HEAD/apps/docs/app/og/[...slug]/JetBrainsMono-Bold.ttf
--------------------------------------------------------------------------------
/apps/docs/app/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Allow: /
3 |
4 | Host: https://stepperize.vercel.app
5 |
6 | Sitemap: https://stepperize.vercel.app/sitemap.xml
7 |
--------------------------------------------------------------------------------
/apps/docs/app/og/[...slug]/JetBrainsMono-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damianricobelli/stepperize/HEAD/apps/docs/app/og/[...slug]/JetBrainsMono-Regular.ttf
--------------------------------------------------------------------------------
/apps/docs/cli.json:
--------------------------------------------------------------------------------
1 | {
2 | "aliases": {
3 | "cn": "./lib/utils.ts",
4 | "componentsDir": "./components",
5 | "uiDir": "./components/ui",
6 | "libDir": "./lib"
7 | }
8 | }
--------------------------------------------------------------------------------
/apps/docs/content/docs/svelte/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Svelte",
3 | "root": true,
4 | "description": "Stepperize for Svelte",
5 | "pages": ["---Getting Started---", "index"]
6 | }
7 |
--------------------------------------------------------------------------------
/packages/react/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "vitest/config";
2 |
3 | export default defineConfig({
4 | test: {
5 | globals: true,
6 | environment: "jsdom",
7 | },
8 | });
9 |
--------------------------------------------------------------------------------
/apps/docs/app/api/search/route.ts:
--------------------------------------------------------------------------------
1 | import { source } from '@/lib/source';
2 | import { createFromSource } from 'fumadocs-core/search/server';
3 |
4 | export const { GET } = createFromSource(source);
5 |
--------------------------------------------------------------------------------
/apps/docs/lib/utils.ts:
--------------------------------------------------------------------------------
1 | import { clsx, type ClassValue } from "clsx"
2 | import { twMerge } from "tailwind-merge"
3 |
4 | export function cn(...inputs: ClassValue[]) {
5 | return twMerge(clsx(inputs))
6 | }
7 |
--------------------------------------------------------------------------------
/packages/react/src/index.ts:
--------------------------------------------------------------------------------
1 | // Types
2 | export type { Step, Stepper, Get } from "@stepperize/core";
3 | export type { StepperReturn, ScopedProps } from "./types";
4 | // Define Stepper
5 | export * from "./define-stepper";
6 |
--------------------------------------------------------------------------------
/packages/vue/src/index.ts:
--------------------------------------------------------------------------------
1 | export type { StepperReturn, ScopedProps } from "./types";
2 | export type { Step, Stepper, Get } from "@stepperize/core";
3 |
4 | export * from "./define-stepper";
5 | export * from "./components";
6 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # These owners will be the default owners for everything in
2 | # the repo. Unless a later match takes precedence, they will
3 | # be requested for review when someone opens a pull request.
4 |
5 | @damianricobelli
6 |
--------------------------------------------------------------------------------
/apps/docs/content/docs/svelte/index.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Introduction
3 | description: A powerful library for creating flexible Stepper components in Svelte
4 | icon: Album
5 | ---
6 |
7 |
8 | Both the documents and the Svelte package are not yet ready.
9 |
10 |
--------------------------------------------------------------------------------
/apps/docs/app/(home)/layout.tsx:
--------------------------------------------------------------------------------
1 | import { baseOptions } from "@/app/layout.config";
2 | import { HomeLayout } from "fumadocs-ui/layouts/home";
3 | import type { ReactNode } from "react";
4 |
5 | export default function Layout({ children }: { children: ReactNode }) {
6 | return {children} ;
7 | }
8 |
--------------------------------------------------------------------------------
/apps/docs/components/steps.tsx:
--------------------------------------------------------------------------------
1 | import type { ReactNode } from 'react';
2 |
3 | export function Steps({ children }: { children: ReactNode }) {
4 | return
{children}
;
5 | }
6 |
7 | export function Step({ children }: { children: ReactNode }) {
8 | return {children}
;
9 | }
10 |
--------------------------------------------------------------------------------
/apps/docs/lib/is-active.ts:
--------------------------------------------------------------------------------
1 | export function isActive(
2 | url: string,
3 | pathname: string,
4 | nested = true,
5 | ): boolean {
6 | if (url.endsWith('/')) url = url.slice(0, -1);
7 | if (pathname.endsWith('/')) pathname = pathname.slice(0, -1);
8 |
9 | return url === pathname || (nested && pathname.startsWith(`${url}/`));
10 | }
11 |
--------------------------------------------------------------------------------
/apps/docs/content/docs/solid/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "React",
3 | "root": true,
4 | "description": "Stepperize for React",
5 | "pages": [
6 | "---Getting Started---",
7 | "index",
8 | "installation",
9 | "---API References---",
10 | "api-references/define",
11 | "api-references/hook",
12 | "api-references/utils"
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/packages/svelte/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "tsup";
2 |
3 | export default defineConfig({
4 | entry: ["src/index.ts"],
5 | format: ["cjs", "esm"],
6 | dts: true,
7 | sourcemap: false,
8 | clean: true,
9 | minify: true,
10 | treeshake: true,
11 | external: ["react", "react-dom"],
12 | tsconfig: "tsconfig.json",
13 | });
14 |
--------------------------------------------------------------------------------
/apps/docs/next.config.mjs:
--------------------------------------------------------------------------------
1 | import { createMDX } from "fumadocs-mdx/next";
2 |
3 | const withMDX = createMDX();
4 |
5 | /** @type {import('next').NextConfig} */
6 | const config = {
7 | reactStrictMode: true,
8 | serverExternalPackages: ["twoslash", "typescript"],
9 | experimental: {
10 | turbopackScopeHoisting: false
11 | }
12 | };
13 |
14 | export default withMDX(config);
15 |
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - "apps/*"
3 | - "packages/*"
4 | catalog:
5 | "@testing-library/jest-dom": ^6.9.1
6 | "@testing-library/react": ^16.3.0
7 | "@types/react": ^19.2.0
8 | "@types/react-dom": ^19.2.0
9 | jsdom: ^27.0.0
10 | react: ^19.2.0
11 | react-dom: ^19.2.0
12 | tsup: ^8.5.0
13 | terser: ^5.44.0
14 | typescript: ^5.9.3
15 | vitest: ^3.2.4
16 |
--------------------------------------------------------------------------------
/apps/docs/components/demo-viewer.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { cva } from "class-variance-authority";
4 |
5 | export const DemoViewer = ({ src, className }: { src: string; className?: string }) => {
6 | return ;
7 | };
8 |
9 | const classForDemoViewer = cva("w-full h-[600px] border rounded-md");
10 |
--------------------------------------------------------------------------------
/.changeset/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://unpkg.com/@changesets/config@3.0.2/schema.json",
3 | "changelog": [
4 | "@changesets/changelog-github",
5 | {
6 | "repo": "damianricobelli/stepperize"
7 | }
8 | ],
9 | "commit": false,
10 | "fixed": [],
11 | "linked": [],
12 | "access": "restricted",
13 | "baseBranch": "main",
14 | "updateInternalDependencies": "patch",
15 | "ignore": []
16 | }
17 |
--------------------------------------------------------------------------------
/apps/docs/registry/new-york/blocks/stepper-demo/page.tsx:
--------------------------------------------------------------------------------
1 | import { StepperDemo } from "@/registry/new-york/blocks/stepper-demo/components/stepper-demo";
2 |
3 | export default async function Page() {
4 | return (
5 |
10 | );
11 | }
12 |
--------------------------------------------------------------------------------
/apps/docs/.gitignore:
--------------------------------------------------------------------------------
1 | # deps
2 | /node_modules
3 |
4 | # generated content
5 | .contentlayer
6 | .content-collections
7 | .source
8 |
9 | # test & build
10 | /coverage
11 | /.next/
12 | /out/
13 | /build
14 | *.tsbuildinfo
15 |
16 | # misc
17 | .DS_Store
18 | *.pem
19 | /.pnp
20 | .pnp.js
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
25 | # others
26 | .env*.local
27 | .vercel
28 | next-env.d.ts
--------------------------------------------------------------------------------
/apps/docs/registry/new-york/blocks/stepper-with-form/page.tsx:
--------------------------------------------------------------------------------
1 | import { StepperWithForm } from "@/registry/new-york/blocks/stepper-with-form/components/stepper-with-form";
2 |
3 | export default async function Page() {
4 | return (
5 |
10 | );
11 | }
12 |
--------------------------------------------------------------------------------
/apps/docs/registry/new-york/blocks/stepper-with-icon/page.tsx:
--------------------------------------------------------------------------------
1 | import { StepperWithIcon } from "@/registry/new-york/blocks/stepper-with-icon/components/stepper-with-icon";
2 |
3 | export default async function Page() {
4 | return (
5 |
10 | );
11 | }
12 |
--------------------------------------------------------------------------------
/apps/docs/content/docs/vue/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Vue",
3 | "root": true,
4 | "description": "Stepperize for Vue",
5 | "pages": [
6 | "---Getting Started---",
7 | "index",
8 | "installation",
9 | "---API References---",
10 | "api-references/define",
11 | "api-references/composable",
12 | "api-references/scoped",
13 | "api-references/utils",
14 | "---Migration---",
15 | "migration/migrating-to-v2"
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/packages/core/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "dist",
4 | "declaration": true,
5 | "declarationMap": true,
6 | "sourceMap": true,
7 | "removeComments": false,
8 | "module": "esnext",
9 | "target": "esnext",
10 | "moduleResolution": "bundler",
11 | "esModuleInterop": true,
12 | "skipLibCheck": true,
13 | "strict": true
14 | },
15 | "include": ["."],
16 | "exclude": ["node_modules", "dist"]
17 | }
18 |
--------------------------------------------------------------------------------
/apps/docs/registry/new-york/blocks/stepper-with-tracking/page.tsx:
--------------------------------------------------------------------------------
1 | import { StepperWithTracking } from "@/registry/new-york/blocks/stepper-with-tracking/components/stepper-with-tracking";
2 |
3 | export default async function Page() {
4 | return (
5 |
10 | );
11 | }
12 |
--------------------------------------------------------------------------------
/apps/docs/registry/new-york/blocks/stepper-with-variants/page.tsx:
--------------------------------------------------------------------------------
1 | import { StepperWithVariants } from "@/registry/new-york/blocks/stepper-with-variants/components/stepper-with-variants";
2 |
3 | export default async function Page() {
4 | return (
5 |
10 | );
11 | }
12 |
--------------------------------------------------------------------------------
/apps/docs/registry/new-york/blocks/stepper-with-description/page.tsx:
--------------------------------------------------------------------------------
1 | import { StepperWithDescription } from "@/registry/new-york/blocks/stepper-with-description/components/stepper-with-description";
2 |
3 | export default async function Page() {
4 | return (
5 |
10 | );
11 | }
12 |
--------------------------------------------------------------------------------
/packages/vue/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "dist",
4 | "declaration": true,
5 | "declarationMap": true,
6 | "sourceMap": true,
7 | "removeComments": false,
8 | "jsx": "react-jsx",
9 | "module": "esnext",
10 | "target": "esnext",
11 | "moduleResolution": "bundler",
12 | "esModuleInterop": true,
13 | "skipLibCheck": true,
14 | "strict": true
15 | },
16 | "include": ["."],
17 | "exclude": ["node_modules", "dist"]
18 | }
19 |
--------------------------------------------------------------------------------
/packages/react/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "dist",
4 | "declaration": true,
5 | "declarationMap": true,
6 | "sourceMap": true,
7 | "removeComments": false,
8 | "jsx": "react-jsx",
9 | "module": "esnext",
10 | "target": "esnext",
11 | "moduleResolution": "bundler",
12 | "esModuleInterop": true,
13 | "skipLibCheck": true,
14 | "strict": true
15 | },
16 | "include": ["."],
17 | "exclude": ["node_modules", "dist"]
18 | }
19 |
--------------------------------------------------------------------------------
/packages/core/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "tsup";
2 |
3 | export default defineConfig({
4 | entry: ["src/index.ts"],
5 | format: ["esm"],
6 | dts: true,
7 | sourcemap: false,
8 | clean: true,
9 | minify: "terser",
10 | treeshake: true,
11 | splitting: true,
12 | tsconfig: "tsconfig.json",
13 | terserOptions: {
14 | compress: {
15 | drop_console: true,
16 | pure_funcs: ["console.info", "console.debug"],
17 | },
18 | mangle: true,
19 | },
20 | });
21 |
--------------------------------------------------------------------------------
/apps/docs/registry/new-york/blocks/stepper-with-label-orientation/page.tsx:
--------------------------------------------------------------------------------
1 | import { StepperWithLabelOrientation } from "@/registry/new-york/blocks/stepper-with-label-orientation/components/stepper-with-label-orientation";
2 |
3 | export default async function Page() {
4 | return (
5 |
10 | );
11 | }
12 |
--------------------------------------------------------------------------------
/apps/docs/lib/source.ts:
--------------------------------------------------------------------------------
1 | import { docs } from "@/.source";
2 | import { loader } from "fumadocs-core/source";
3 | import { icons } from "lucide-react";
4 | import { createElement } from "react";
5 |
6 | export const source = loader({
7 | baseUrl: "/docs",
8 | source: docs.toFumadocsSource(),
9 | icon(icon) {
10 | if (!icon) {
11 | // You may set a default icon
12 | return;
13 | }
14 |
15 | if (icon in icons) return createElement(icons[icon as keyof typeof icons]);
16 | },
17 | });
18 |
--------------------------------------------------------------------------------
/apps/docs/registry/new-york/blocks/stepper-with-responsive-variant/page.tsx:
--------------------------------------------------------------------------------
1 | import { StepperWithResponsiveVariant } from "@/registry/new-york/blocks/stepper-with-responsive-variant/components/stepper-with-responsive-variant";
2 |
3 | export default async function Page() {
4 | return (
5 |
10 | );
11 | }
12 |
--------------------------------------------------------------------------------
/packages/solid/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "dist",
4 | "declaration": true,
5 | "declarationMap": true,
6 | "sourceMap": true,
7 | "removeComments": false,
8 | "jsx": "react-jsx",
9 | "module": "esnext",
10 | "target": "esnext",
11 | "moduleResolution": "bundler",
12 | "esModuleInterop": true,
13 | "skipLibCheck": true,
14 | "strict": true
15 | },
16 | "include": ["."],
17 | "exclude": ["node_modules", "dist"]
18 | }
19 |
--------------------------------------------------------------------------------
/apps/docs/content/docs/react/examples/conform-react.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Conform React
3 | description: Stepperize with Conform library.
4 | ---
5 |
6 | This example demonstrates how to use the `@stepperize/react` with `@conform-to/react` to create a stepperized form.
7 |
8 | ## Live Demo
9 |
10 |
11 |
12 | ## Additional Resources
13 |
14 | - [Conform Documentation](https://conform.guide/)
--------------------------------------------------------------------------------
/packages/svelte/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "dist",
4 | "declaration": true,
5 | "declarationMap": true,
6 | "sourceMap": true,
7 | "removeComments": false,
8 | "jsx": "react-jsx",
9 | "module": "esnext",
10 | "target": "esnext",
11 | "moduleResolution": "bundler",
12 | "esModuleInterop": true,
13 | "skipLibCheck": true,
14 | "strict": true
15 | },
16 | "include": [
17 | "."
18 | ],
19 | "exclude": [
20 | "node_modules",
21 | "dist"
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/packages/vue/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "tsup";
2 |
3 | export default defineConfig({
4 | entry: ["src/index.ts"],
5 | format: ["esm"],
6 | dts: true,
7 | sourcemap: false,
8 | clean: true,
9 | minify: "terser",
10 | treeshake: true,
11 | splitting: true,
12 | external: ["vue"],
13 | tsconfig: "tsconfig.json",
14 | terserOptions: {
15 | compress: {
16 | drop_console: true,
17 | pure_funcs: ["console.info", "console.debug"],
18 | },
19 | mangle: true,
20 | },
21 | });
22 |
--------------------------------------------------------------------------------
/packages/solid/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "tsup";
2 |
3 | export default defineConfig({
4 | entry: ["src/index.ts"],
5 | format: ["esm"],
6 | dts: true,
7 | sourcemap: false,
8 | clean: true,
9 | minify: "terser",
10 | treeshake: true,
11 | splitting: true,
12 | external: ["solid-js"],
13 | tsconfig: "tsconfig.json",
14 | terserOptions: {
15 | compress: {
16 | drop_console: true,
17 | pure_funcs: ["console.info", "console.debug"],
18 | },
19 | mangle: true,
20 | },
21 | });
22 |
--------------------------------------------------------------------------------
/apps/docs/content/docs/react/examples/react-hook-form.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: React Hook Form
3 | description: Stepperize with React Hook Form library.
4 | ---
5 |
6 | This example demonstrates how to use `@stepperize/react` with `react-hook-form` to create a stepperized form.
7 |
8 | ## Live Demo
9 |
10 |
11 |
12 | ## Additional Resources
13 |
14 | - [React Hook Form Documentation](https://react-hook-form.com/)
--------------------------------------------------------------------------------
/packages/react/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "tsup";
2 |
3 | export default defineConfig({
4 | entry: ["src/index.ts"],
5 | format: ["esm"],
6 | dts: true,
7 | sourcemap: false,
8 | clean: true,
9 | minify: "terser",
10 | treeshake: true,
11 | splitting: true,
12 | external: ["react", "react-dom"],
13 | tsconfig: "tsconfig.json",
14 | terserOptions: {
15 | compress: {
16 | drop_console: true,
17 | pure_funcs: ["console.info", "console.debug"],
18 | },
19 | mangle: true,
20 | },
21 | });
22 |
--------------------------------------------------------------------------------
/apps/docs/components.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://ui.shadcn.com/schema.json",
3 | "style": "new-york",
4 | "rsc": true,
5 | "tsx": true,
6 | "tailwind": {
7 | "config": "",
8 | "css": "app/global.css",
9 | "baseColor": "neutral",
10 | "cssVariables": true,
11 | "prefix": ""
12 | },
13 | "aliases": {
14 | "components": "@/components",
15 | "utils": "@/lib/utils",
16 | "ui": "@/components/ui",
17 | "lib": "@/lib",
18 | "hooks": "@/hooks"
19 | },
20 | "iconLibrary": "lucide"
21 | }
--------------------------------------------------------------------------------
/.changeset/README.md:
--------------------------------------------------------------------------------
1 | # Changesets
2 |
3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
4 | with multi-package repos, or single-package repos to help you version and publish your code. You can
5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets)
6 |
7 | We have a quick list of common questions to get you started engaging with this project in
8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
9 |
--------------------------------------------------------------------------------
/turbo.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://turbo.build/schema.json",
3 | "tasks": {
4 | "build": {
5 | "dependsOn": ["^build"],
6 | "inputs": ["$TURBO_DEFAULT$", ".env*"],
7 | "outputs": [".next/**", "!.next/cache/**", "dist/**"]
8 | },
9 | "lint": {
10 | "dependsOn": ["^lint"]
11 | },
12 | "//#format-and-lint": {},
13 | "//#format-and-lint:fix": {
14 | "cache": false
15 | },
16 | "dev": {
17 | "cache": false,
18 | "persistent": true,
19 | "dependsOn": ["^build"]
20 | },
21 | "clean": {
22 | "cache": false
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/apps/docs/content/docs/react/shadcn/introduction.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Introduction
3 | description: Discover shadcn-stepper, a powerful and customizable stepper component for shadcn/ui projects
4 | ---
5 |
6 | ## What is shadcn-stepper?
7 |
8 | Is an abstraction of the [@stepperize/react](https://stepperize.vercel.app/docs/react) library to allow the shadcn/ui community to use the stepper component in their projects.
9 |
10 | ## Why shadcn-stepper?
11 |
12 | Because shadcn/ui needs a component that allows us to create flows of steps in a simple way and with a high level of customisation along with type safety
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # Dependencies
4 | node_modules
5 | .pnp
6 | .pnp.js
7 |
8 | # Local env files
9 | .env
10 | .env.local
11 | .env.development.local
12 | .env.test.local
13 | .env.production.local
14 |
15 | # Testing
16 | coverage
17 |
18 | # Turbo
19 | .turbo
20 |
21 | # Vercel
22 | .vercel
23 |
24 | # Build Outputs
25 | .next/
26 | out/
27 | build
28 | dist
29 |
30 |
31 | # Debug
32 | npm-debug.log*
33 | yarn-debug.log*
34 | yarn-error.log*
35 |
36 | # Misc
37 | .DS_Store
38 | *.pem
39 |
40 | # PNPM
41 | .pnpm-store
42 |
--------------------------------------------------------------------------------
/apps/docs/registry/new-york/blocks/stepper-with-responsive-variant/hooks/use-media-query.ts:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 |
3 | export function useMediaQuery(query: string) {
4 | const [value, setValue] = React.useState(false)
5 |
6 | React.useEffect(() => {
7 | function onChange(event: MediaQueryListEvent) {
8 | setValue(event.matches)
9 | }
10 |
11 | const result = matchMedia(query)
12 | result.addEventListener("change", onChange)
13 | setValue(result.matches)
14 |
15 | return () => result.removeEventListener("change", onChange)
16 | }, [query])
17 |
18 | return value
19 | }
20 |
--------------------------------------------------------------------------------
/apps/docs/source.config.ts:
--------------------------------------------------------------------------------
1 | import { rehypeCodeDefaultOptions } from "fumadocs-core/mdx-plugins";
2 | import { defineConfig, defineDocs } from "fumadocs-mdx/config";
3 | import { transformerTwoslash } from "fumadocs-twoslash";
4 |
5 | export const docs = defineDocs({
6 | dir: "content/docs",
7 | });
8 |
9 | export default defineConfig({
10 | lastModifiedTime: "git",
11 | mdxOptions: {
12 | rehypeCodeOptions: {
13 | themes: {
14 | light: "github-light",
15 | dark: "github-dark",
16 | },
17 | transformers: [...(rehypeCodeDefaultOptions.transformers ?? []), transformerTwoslash()],
18 | },
19 | },
20 | });
21 |
--------------------------------------------------------------------------------
/packages/svelte/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@stepperize/svelte",
3 | "version": "0.1.5",
4 | "private": true,
5 | "type": "module",
6 | "main": "./dist/index.js",
7 | "module": "./dist/index.js",
8 | "exports": {
9 | ".": "./dist/index.js"
10 | },
11 | "sideEffects": false,
12 | "files": [
13 | "dist"
14 | ],
15 | "scripts": {
16 | "build": "tsup",
17 | "dev": "tsup --watch",
18 | "lint": "turbo lint",
19 | "prepublishOnly": "pnpm run build",
20 | "clean": "rm -rf .turbo && rm -rf node_modules dist"
21 | },
22 | "devDependencies": {
23 | "tsup": "catalog:",
24 | "typescript": "catalog:"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/apps/docs/README.md:
--------------------------------------------------------------------------------
1 | # docs2
2 |
3 | This is a Next.js application generated with
4 | [Create Fumadocs](https://github.com/fuma-nama/fumadocs).
5 |
6 | Run development server:
7 |
8 | ```bash
9 | npm run dev
10 | # or
11 | pnpm dev
12 | # or
13 | yarn dev
14 | ```
15 |
16 | Open http://localhost:3000 with your browser to see the result.
17 |
18 | ## Learn More
19 |
20 | To learn more about Next.js and Fumadocs, take a look at the following
21 | resources:
22 |
23 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js
24 | features and API.
25 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
26 | - [Fumadocs](https://fumadocs.vercel.app) - learn about Fumadocs
27 |
--------------------------------------------------------------------------------
/apps/docs/content/docs/vue/installation.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Installation
3 | description: How to install and set up @stepperize/vue in your project
4 | icon: Rocket
5 | ---
6 |
7 | Choose your preferred package manager to install `@stepperize/vue`:
8 |
9 |
10 | ```bash npm install @stepperize/vue ```
11 | ```bash yarn add @stepperize/vue ```
12 | ```bash pnpm install @stepperize/vue ```
13 | ```bash bun add @stepperize/vue ```
14 |
15 |
16 | ## Next Steps
17 |
18 | - [API References](/docs/vue/api-references/define)
19 | - Examples (coming soon)
20 |
--------------------------------------------------------------------------------
/apps/docs/content/docs/react/migration/migrating-to-v5.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Migrating to v5
3 | description: Learn how to migrate from v4 to v5
4 | icon: CircleFadingArrowUp
5 | ---
6 |
7 | Stepperize v5 adds a new `metadata` API to the `useStepper` hook
8 |
9 | ## Breaking Changes
10 |
11 | ### Name changes in the values returned by the hook
12 |
13 | Now the parameters of useStepper are not a string indicating the initial step, but rather an object with the following values:
14 |
15 | - `initialStep` The ID of the initial step to display
16 | - `initialMetadata` The initial metadata to set for the steps
17 |
18 | ## New Features
19 |
20 | ### New `metadata` API
21 |
22 | The `metadata` API allows you to set dynamic metadata for each step.
23 |
--------------------------------------------------------------------------------
/apps/docs/content/docs/solid/installation.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Installation
3 | description: How to install and set up @stepperize/solid in your project
4 | icon: Rocket
5 | ---
6 |
7 | Choose your preferred package manager to install `@stepperize/solid`:
8 |
9 |
10 | ```bash npm install @stepperize/solid ```
11 | ```bash yarn add @stepperize/solid ```
12 | ```bash pnpm install @stepperize/solid ```
13 | ```bash bun add @stepperize/solid ```
14 |
15 |
16 | ## Next Steps
17 |
18 | - [API References](/docs/solid/api-references/define)
19 | - Examples (coming soon)
20 |
--------------------------------------------------------------------------------
/apps/docs/content/docs/react/installation.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Installation
3 | description: How to install and set up @stepperize/react in your project
4 | icon: Rocket
5 | ---
6 |
7 | Choose your preferred package manager to install `@stepperize/react`:
8 |
9 |
10 | ```bash npm install @stepperize/react ```
11 | ```bash yarn add @stepperize/react ```
12 | ```bash pnpm install @stepperize/react ```
13 | ```bash bun add @stepperize/react ```
14 |
15 |
16 | ## Next Steps
17 |
18 | - [API References](/docs/react/api-references/define)
19 | - [Examples](/docs/react/examples)
20 |
--------------------------------------------------------------------------------
/apps/docs/content/docs/vue/migration/migrating-to-v2.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Migrating to v2
3 | description: Learn how to migrate from v1 to v2
4 | icon: CircleFadingArrowUp
5 | ---
6 |
7 | `@stepperize/vue` adds a new `metadata` API to the `useStepper` composable.
8 |
9 | ## Breaking Changes
10 |
11 | ### Name changes in the values returned by the composable
12 |
13 | Now the parameters of useStepper are not a string indicating the initial step, but rather an object with the following values:
14 |
15 | - `initialStep` The ID of the initial step to display
16 | - `initialMetadata` The initial metadata to set for the steps
17 |
18 | ## New Features
19 |
20 | ### New `metadata` API
21 |
22 | The `metadata` API allows you to set dynamic metadata for each step.
23 |
--------------------------------------------------------------------------------
/apps/docs/content/docs/react/examples/scoped.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Scoped Stepper
3 | description: Use the Stepper component with scoped children for a more customized experience.
4 | ---
5 |
6 | The Scoped Stepper allows you to create a multi-step process with custom content for each step. This example demonstrates how to use the Stepper component with scoped children, giving you more control over the content and layout of each step.
7 |
8 | Key Features
9 |
10 | - Custom content for each step
11 | - Flexible layout options
12 | - Easy integration with your React components
13 |
14 | ## Live Demo
15 |
16 |
20 |
--------------------------------------------------------------------------------
/apps/docs/app/sitemap.ts:
--------------------------------------------------------------------------------
1 | import { baseUrl } from "@/lib/metadata";
2 | import { source } from "@/lib/source";
3 | import type { MetadataRoute } from "next";
4 |
5 | export default function sitemap(): MetadataRoute.Sitemap {
6 | const url = (path: string): string => new URL(path, baseUrl).toString();
7 |
8 | return [
9 | {
10 | url: url("/"),
11 | changeFrequency: "monthly",
12 | priority: 1,
13 | },
14 | {
15 | url: url("/docs"),
16 | changeFrequency: "monthly",
17 | priority: 0.8,
18 | },
19 | ...source.getPages().map((page) => ({
20 | url: url(page.url),
21 | lastModified: page.data.lastModified ? new Date(page.data.lastModified) : undefined,
22 | changeFrequency: "weekly",
23 | priority: 0.5,
24 | })),
25 | ];
26 | }
27 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "stepperize",
3 | "private": true,
4 | "repository": {
5 | "type": "git",
6 | "url": "https://github.com/damianricobelli/stepperize.git"
7 | },
8 | "scripts": {
9 | "build": "turbo build",
10 | "dev": "turbo dev",
11 | "lint": "turbo lint",
12 | "clean": "turbo run clean && rm -rf node_modules .pnpm-store .turbo",
13 | "format-and-lint": "biome check .",
14 | "format-and-lint:fix": "biome check . --write",
15 | "ci:version": "changeset version",
16 | "ci:release": "changeset publish"
17 | },
18 | "devDependencies": {
19 | "@biomejs/biome": "2.2.5",
20 | "@changesets/changelog-github": "0.5.1",
21 | "@changesets/cli": "^2.29.7",
22 | "turbo": "2.5.8",
23 | "typescript": "catalog:"
24 | },
25 | "packageManager": "pnpm@10.18.1",
26 | "engines": {
27 | "node": ">=18"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/apps/docs/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": ".",
4 | "target": "ESNext",
5 | "lib": ["dom", "dom.iterable", "esnext"],
6 | "allowJs": true,
7 | "skipLibCheck": true,
8 | "strict": true,
9 | "forceConsistentCasingInFileNames": true,
10 | "noEmit": true,
11 | "esModuleInterop": true,
12 | "module": "esnext",
13 | "moduleResolution": "bundler",
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "jsx": "preserve",
17 | "incremental": true,
18 | "paths": {
19 | "@/.source": ["./.source/index.ts"],
20 | "@/*": ["./*"]
21 | },
22 | "plugins": [
23 | {
24 | "name": "next"
25 | }
26 | ]
27 | },
28 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
29 | "exclude": ["node_modules"]
30 | }
31 |
--------------------------------------------------------------------------------
/apps/docs/content/docs/react/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "React",
3 | "root": true,
4 | "description": "Stepperize for React",
5 | "pages": [
6 | "---Getting Started---",
7 | "index",
8 | "installation",
9 | "---API References---",
10 | "api-references/define",
11 | "api-references/hook",
12 | "api-references/scoped",
13 | "api-references/utils",
14 | "api-references/schema-validation",
15 | "---Shadcn integration---",
16 | "shadcn/introduction",
17 | "shadcn/installation",
18 | "shadcn/api",
19 | "shadcn/examples",
20 | "---Examples---",
21 | "examples/basic",
22 | "examples/scoped",
23 | "examples/multi-scoped",
24 | "examples/conform-react",
25 | "examples/react-hook-form",
26 | "---Migration---",
27 | "migration/migrating-to-v5",
28 | "migration/migrating-to-v3",
29 | "migration/migrating-to-v2"
30 | ]
31 | }
32 |
--------------------------------------------------------------------------------
/apps/docs/registry/new-york/ui/label.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as LabelPrimitive from "@radix-ui/react-label";
4 | import { type VariantProps, cva } from "class-variance-authority";
5 | import * as React from "react";
6 |
7 | import { cn } from "@/lib/utils";
8 |
9 | const labelVariants = cva(
10 | "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
11 | );
12 |
13 | const Label = React.forwardRef<
14 | React.ElementRef,
15 | React.ComponentPropsWithoutRef &
16 | VariantProps
17 | >(({ className, ...props }, ref) => (
18 |
23 | ));
24 | Label.displayName = LabelPrimitive.Root.displayName;
25 |
26 | export { Label };
27 |
--------------------------------------------------------------------------------
/apps/docs/app/favicon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/apps/docs/lib/metadata.ts:
--------------------------------------------------------------------------------
1 | import type { Metadata } from "next/types";
2 |
3 | export function createMetadata(override: Metadata): Metadata {
4 | return {
5 | ...override,
6 | openGraph: {
7 | title: override.title ?? undefined,
8 | description: override.description ?? undefined,
9 | url: "https://stepperize.vercel.app",
10 | images: "/banner.png",
11 | siteName: "Stepperize",
12 | ...override.openGraph,
13 | },
14 | twitter: {
15 | card: "summary_large_image",
16 | creator: "@damianricobelli",
17 | title: override.title ?? undefined,
18 | description: override.description ?? undefined,
19 | images: "/banner.png",
20 | ...override.twitter,
21 | },
22 | };
23 | }
24 |
25 | export const baseUrl =
26 | process.env.NODE_ENV === "development" || !process.env.VERCEL_URL
27 | ? new URL("http://localhost:3000")
28 | : new URL(`https://${process.env.VERCEL_URL}`);
29 |
--------------------------------------------------------------------------------
/apps/docs/content/docs/react/examples/multi-scoped.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Multi-scoped Stepper
3 | description: Implement a Stepper component with multiple scoped containers for advanced UI workflows.
4 | ---
5 |
6 | The Multi-scoped Stepper allows you to create complex, multi-level workflows within your React application. This component is particularly useful for:
7 |
8 | - Multi-step forms with nested sections
9 | - Wizard-like interfaces with branching paths
10 | - Hierarchical navigation structures
11 |
12 | Key Features
13 |
14 | - Nested step structure
15 | - Independent progress tracking for each scope
16 | - Customizable step content and navigation
17 |
18 | ## Live Demo
19 |
20 | Interact with the Multi-scoped Stepper example below:
21 |
22 |
26 |
--------------------------------------------------------------------------------
/apps/docs/components/ui/button.tsx:
--------------------------------------------------------------------------------
1 | import { cva } from 'class-variance-authority';
2 |
3 | export const buttonVariants = cva(
4 | 'inline-flex items-center justify-center rounded-md p-2 text-sm font-medium transition-colors duration-100 disabled:pointer-events-none disabled:opacity-50',
5 | {
6 | variants: {
7 | color: {
8 | primary:
9 | 'bg-fd-primary text-fd-primary-foreground hover:bg-fd-primary/80',
10 | outline: 'border hover:bg-fd-accent hover:text-fd-accent-foreground',
11 | ghost: 'hover:bg-fd-accent hover:text-fd-accent-foreground',
12 | secondary:
13 | 'border bg-fd-secondary text-fd-secondary-foreground hover:bg-fd-accent hover:text-fd-accent-foreground',
14 | },
15 | size: {
16 | sm: 'gap-1 p-0.5 text-xs',
17 | icon: 'p-1.5 [&_svg]:size-5',
18 | 'icon-sm': 'p-1.5 [&_svg]:size-4.5',
19 | },
20 | },
21 | },
22 | );
23 |
--------------------------------------------------------------------------------
/biome.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://biomejs.dev/schemas/2.1.3/schema.json",
3 | "files": {
4 | "includes": ["**", "!.turbo", "!.next", "!.changeset", "!.pnpm-store", "!packages/react/dist"]
5 | },
6 | "assist": { "actions": { "source": { "organizeImports": "on" } } },
7 | "formatter": {
8 | "lineWidth": 120
9 | },
10 | "linter": {
11 | "enabled": true,
12 | "rules": {
13 | "recommended": true,
14 | "a11y": {
15 | "useKeyWithClickEvents": "off"
16 | },
17 | "nursery": {
18 | "useSortedClasses": "off"
19 | },
20 | "style": {
21 | "useBlockStatements": "off"
22 | },
23 | "suspicious": {
24 | "noExplicitAny": "off",
25 | "noArrayIndexKey": "off",
26 | "noConsole": { "level": "error", "options": { "allow": ["log"] } }
27 | },
28 | "correctness": {
29 | "noUnusedVariables": "error",
30 | "noUnusedImports": "error",
31 | "useExhaustiveDependencies": "off"
32 | }
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/apps/docs/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import "./global.css";
2 | import { baseUrl, createMetadata } from "@/lib/metadata";
3 | import { RootProvider } from "fumadocs-ui/provider";
4 | import { GeistMono } from "geist/font/mono";
5 | import { GeistSans } from "geist/font/sans";
6 | import type { ReactNode } from "react";
7 |
8 | export const metadata = createMetadata({
9 | title: {
10 | template: "%s | Stepperize",
11 | default: "Stepperize",
12 | },
13 | description: "The type-safe library for building step-by-step workflows",
14 | metadataBase: baseUrl,
15 | });
16 |
17 | export default function Layout({ children }: { children: ReactNode }) {
18 | return (
19 |
24 |
25 | {children}
26 |
27 |
28 | );
29 | }
30 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
--------------------------------------------------------------------------------
/apps/docs/lib/use-copy-button.ts:
--------------------------------------------------------------------------------
1 | 'use client';
2 | import {
3 | useState,
4 | useRef,
5 | useEffect,
6 | useCallback,
7 | type MouseEventHandler,
8 | } from 'react';
9 |
10 | export function useCopyButton(
11 | onCopy: () => void,
12 | ): [checked: boolean, onClick: MouseEventHandler] {
13 | const [checked, setChecked] = useState(false);
14 | const timeoutRef = useRef(null);
15 | const callbackRef = useRef(onCopy);
16 | callbackRef.current = onCopy;
17 |
18 | const onClick: MouseEventHandler = useCallback(() => {
19 | if (timeoutRef.current) window.clearTimeout(timeoutRef.current);
20 | timeoutRef.current = window.setTimeout(() => {
21 | setChecked(false);
22 | }, 1500);
23 | callbackRef.current();
24 | setChecked(true);
25 | }, []);
26 |
27 | // Avoid updates after being unmounted
28 | useEffect(() => {
29 | return () => {
30 | if (timeoutRef.current) window.clearTimeout(timeoutRef.current);
31 | };
32 | }, []);
33 |
34 | return [checked, onClick];
35 | }
36 |
--------------------------------------------------------------------------------
/apps/docs/components/heading.tsx:
--------------------------------------------------------------------------------
1 | import { Link } from 'lucide-react';
2 | import type { ComponentPropsWithoutRef } from 'react';
3 | import { cn } from '../lib/cn';
4 |
5 | type Types = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
6 | type HeadingProps = Omit, 'as'> & {
7 | as?: T;
8 | };
9 |
10 | export function Heading({
11 | as,
12 | className,
13 | ...props
14 | }: HeadingProps): React.ReactElement {
15 | const As = as ?? 'h1';
16 |
17 | if (!props.id) return ;
18 |
19 | return (
20 |
24 |
25 | {props.children}
26 |
27 |
31 |
32 | );
33 | }
34 |
--------------------------------------------------------------------------------
/apps/docs/registry/new-york/ui/input.tsx:
--------------------------------------------------------------------------------
1 | import type * as React from "react";
2 |
3 | import { cn } from "@/lib/utils";
4 |
5 | function Input({ className, type, ...props }: React.ComponentProps<"input">) {
6 | return (
7 |
18 | );
19 | }
20 |
21 | export { Input };
22 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | ## Description
2 |
3 | Please provide a brief description of the changes introduced in this pull request.
4 |
5 | - What issue does this pull request address?
6 | - What does it fix, improve, or add?
7 |
8 | ## Related Issues
9 |
10 | List any related issues, bugs, or feature requests that this pull request addresses.
11 |
12 | - Issue: [#issue_number](link_to_issue)
13 |
14 | ## Checklist
15 |
16 | Please review the following checklist before submitting the pull request:
17 |
18 | - [ ] My code follows the code style of this project.
19 | - [ ] I have added tests to cover my changes.
20 | - [ ] All new and existing tests passed.
21 | - [ ] I have updated the documentation (if necessary).
22 | - [ ] I have checked the build and it works as expected.
23 |
24 | ## Screenshots (if appropriate)
25 |
26 | Include screenshots or videos to demonstrate the visual changes introduced by this pull request.
27 |
28 | ## Additional Notes
29 |
30 | Provide any additional information that might be helpful for the reviewer, such as implementation details or potential issues.
31 |
--------------------------------------------------------------------------------
/packages/vue/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@stepperize/vue",
3 | "version": "2.0.9",
4 | "private": false,
5 | "publishConfig": {
6 | "access": "public"
7 | },
8 | "description": "The library for building step-by-step workflows for Vue apps",
9 | "author": "damianricobelli",
10 | "license": "MIT",
11 | "repository": {
12 | "type": "git",
13 | "url": "https://github.com/damianricobelli/stepperize.git",
14 | "directory": "packages/vue"
15 | },
16 | "homepage": "https://stepperize.vercel.app",
17 | "sideEffects": false,
18 | "files": [
19 | "dist"
20 | ],
21 | "type": "module",
22 | "main": "./dist/index.js",
23 | "module": "./dist/index.js",
24 | "exports": {
25 | ".": "./dist/index.js"
26 | },
27 | "scripts": {
28 | "build": "tsup",
29 | "dev": "tsup --watch",
30 | "lint": "turbo lint",
31 | "prepublishOnly": "pnpm run build",
32 | "clean": "rm -rf .turbo && rm -rf node_modules dist"
33 | },
34 | "dependencies": {
35 | "@stepperize/core": "workspace:*"
36 | },
37 | "peerDependencies": {
38 | "vue": ">=3.3.0"
39 | },
40 | "devDependencies": {
41 | "tsup": "catalog:",
42 | "terser": "catalog:",
43 | "typescript": "catalog:",
44 | "vue": "3.5.22"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to Stepperize
2 |
3 | We're glad you're interested in contributing to our project! Here are some guidelines to help you get started.
4 |
5 | ## Getting Started
6 |
7 | 1. Fork the repository
8 | 2. Clone your fork: `git clone https://github.com/damianricobelli/stepperize.git`
9 | 3. Create a new branch: `git checkout -b your-feature-name`
10 | 4. Make your changes
11 | 5. Commit your changes: `git commit -m "Add some feature"`
12 | 6. Push to the branch: `git push origin your-feature-name`
13 | 7. Create a pull request
14 |
15 | ## Development Setup
16 |
17 | 1. Install dependencies: `pnpm i`
18 | 2. Run the development docs server: `pnpm dev --filter docs`
19 |
20 | ## Code Style
21 |
22 | We use Biome for code formatting. Please ensure your code is formatted before submitting a pull request.
23 |
24 | ## Submitting Pull Requests
25 |
26 | 1. Ensure your code adheres to the existing style.
27 | 2. Include tests for any new functionality.
28 | 3. Update documentation if necessary.
29 | 4. Describe your changes in detail in the pull request description.
30 |
31 | ## Reporting Issues
32 |
33 | Use the GitHub issue tracker to report bugs or suggest features.
34 |
35 | Thank you for contributing!
--------------------------------------------------------------------------------
/packages/solid/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@stepperize/solid",
3 | "version": "1.0.7",
4 | "private": false,
5 | "publishConfig": {
6 | "access": "public"
7 | },
8 | "description": "The library for building step-by-step workflows for Solid JS apps",
9 | "author": "damianricobelli",
10 | "license": "MIT",
11 | "repository": {
12 | "type": "git",
13 | "url": "https://github.com/damianricobelli/stepperize.git",
14 | "directory": "packages/solid"
15 | },
16 | "homepage": "https://stepperize.vercel.app",
17 | "sideEffects": false,
18 | "files": [
19 | "dist"
20 | ],
21 | "type": "module",
22 | "main": "./dist/index.js",
23 | "module": "./dist/index.js",
24 | "exports": {
25 | ".": "./dist/index.js"
26 | },
27 | "scripts": {
28 | "build": "tsup",
29 | "dev": "tsup --watch",
30 | "lint": "turbo lint",
31 | "prepublishOnly": "pnpm run build",
32 | "clean": "rm -rf .turbo && rm -rf node_modules dist"
33 | },
34 | "dependencies": {
35 | "@stepperize/core": "workspace:*"
36 | },
37 | "peerDependencies": {
38 | "solid-js": ">=1.0.0"
39 | },
40 | "devDependencies": {
41 | "solid-js": "^1.9.9",
42 | "tsup": "catalog:",
43 | "terser": "catalog:",
44 | "typescript": "catalog:"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/apps/docs/app/og/[...slug]/route.tsx:
--------------------------------------------------------------------------------
1 | import { readFileSync } from "node:fs";
2 | import { generateOGImage } from "@/app/og/[...slug]/og";
3 | import { source } from "@/lib/source";
4 | import { notFound } from "next/navigation";
5 |
6 | const font = readFileSync("./app/og/[...slug]/JetBrainsMono-Regular.ttf");
7 | const fontBold = readFileSync("./app/og/[...slug]/JetBrainsMono-Bold.ttf");
8 |
9 | export async function GET(
10 | _req: Request,
11 | { params }: { params: Promise<{ slug: string[] }> }
12 | ) {
13 | const { slug } = await params;
14 | const page = source.getPage(slug.slice(0, -1));
15 |
16 | if (!page) notFound();
17 |
18 | return generateOGImage({
19 | primaryTextColor: "rgb(240,240,240)",
20 | title: page.data.title,
21 | description: page.data.description,
22 | fonts: [
23 | {
24 | name: "Mono",
25 | data: font,
26 | weight: 400,
27 | },
28 | {
29 | name: "Mono",
30 | data: fontBold,
31 | weight: 600,
32 | },
33 | ],
34 | });
35 | }
36 |
37 | export function generateStaticParams(): {
38 | slug: string[];
39 | }[] {
40 | return source.generateParams().map((page) => ({
41 | ...page,
42 | slug: [...page.slug, "image.png"],
43 | }));
44 | }
45 |
--------------------------------------------------------------------------------
/apps/docs/components/ui/collapsible.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 | import * as CollapsiblePrimitive from '@radix-ui/react-collapsible';
3 | import { forwardRef, useEffect, useState } from 'react';
4 | import { cn } from '../../lib/cn';
5 |
6 | const Collapsible = CollapsiblePrimitive.Root;
7 |
8 | const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger;
9 |
10 | const CollapsibleContent = forwardRef<
11 | HTMLDivElement,
12 | React.ComponentPropsWithoutRef
13 | >(({ children, ...props }, ref) => {
14 | const [mounted, setMounted] = useState(false);
15 |
16 | useEffect(() => {
17 | setMounted(true);
18 | }, []);
19 |
20 | return (
21 |
31 | {children}
32 |
33 | );
34 | });
35 |
36 | CollapsibleContent.displayName =
37 | CollapsiblePrimitive.CollapsibleContent.displayName;
38 |
39 | export { Collapsible, CollapsibleTrigger, CollapsibleContent };
40 |
--------------------------------------------------------------------------------
/apps/docs/components/ui/popover.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 | import * as PopoverPrimitive from '@radix-ui/react-popover';
3 | import * as React from 'react';
4 | import { cn } from '../../lib/cn';
5 |
6 | const Popover = PopoverPrimitive.Root;
7 |
8 | const PopoverTrigger = PopoverPrimitive.Trigger;
9 |
10 | const PopoverContent = React.forwardRef<
11 | React.ComponentRef,
12 | React.ComponentPropsWithoutRef
13 | >(({ className, align = 'center', sideOffset = 4, ...props }, ref) => (
14 |
15 |
26 |
27 | ));
28 | PopoverContent.displayName = PopoverPrimitive.Content.displayName;
29 |
30 | const PopoverClose = PopoverPrimitive.PopoverClose;
31 |
32 | export { Popover, PopoverTrigger, PopoverContent, PopoverClose };
33 |
--------------------------------------------------------------------------------
/packages/core/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@stepperize/core",
3 | "version": "1.2.7",
4 | "private": false,
5 | "publishConfig": {
6 | "access": "public"
7 | },
8 | "description": "Core utilities and TypeScript types for building step-based workflows with stepperize",
9 | "author": "damianricobelli",
10 | "license": "MIT",
11 | "repository": {
12 | "type": "git",
13 | "url": "https://github.com/damianricobelli/stepperize.git",
14 | "directory": "packages/core"
15 | },
16 | "homepage": "https://stepperize.vercel.app",
17 | "sideEffects": false,
18 | "files": [
19 | "dist"
20 | ],
21 | "type": "module",
22 | "main": "./dist/index.js",
23 | "module": "./dist/index.js",
24 | "exports": {
25 | ".": "./dist/index.js"
26 | },
27 | "scripts": {
28 | "build": "tsup",
29 | "dev": "tsup --watch",
30 | "lint": "turbo lint",
31 | "prepublishOnly": "pnpm run build",
32 | "clean": "rm -rf .turbo && rm -rf node_modules dist",
33 | "test": "vitest"
34 | },
35 | "peerDependencies": {
36 | "typescript": ">=5.0.2"
37 | },
38 | "devDependencies": {
39 | "@testing-library/jest-dom": "catalog:",
40 | "@testing-library/react": "catalog:",
41 | "jsdom": "catalog:",
42 | "tsup": "catalog:",
43 | "terser": "catalog:",
44 | "typescript": "catalog:",
45 | "vitest": "catalog:"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/packages/react/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@stepperize/react",
3 | "version": "5.1.9",
4 | "private": false,
5 | "publishConfig": {
6 | "access": "public"
7 | },
8 | "description": "The library for building step-by-step workflows for React and React Native apps",
9 | "author": "damianricobelli",
10 | "license": "MIT",
11 | "repository": {
12 | "type": "git",
13 | "url": "https://github.com/damianricobelli/stepperize.git",
14 | "directory": "packages/react"
15 | },
16 | "homepage": "https://stepperize.vercel.app",
17 | "sideEffects": false,
18 | "files": [
19 | "dist"
20 | ],
21 | "type": "module",
22 | "main": "./dist/index.js",
23 | "module": "./dist/index.js",
24 | "exports": {
25 | ".": "./dist/index.js"
26 | },
27 | "scripts": {
28 | "build": "tsup",
29 | "dev": "tsup --watch",
30 | "lint": "turbo lint",
31 | "prepublishOnly": "pnpm run build",
32 | "clean": "rm -rf .turbo && rm -rf node_modules dist",
33 | "test": "vitest"
34 | },
35 | "dependencies": {
36 | "@stepperize/core": "workspace:*"
37 | },
38 | "peerDependencies": {
39 | "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
40 | },
41 | "devDependencies": {
42 | "@testing-library/jest-dom": "catalog:",
43 | "@testing-library/react": "catalog:",
44 | "@types/react": "catalog:",
45 | "jsdom": "catalog:",
46 | "react": "catalog:",
47 | "terser": "catalog:",
48 | "tsup": "catalog:",
49 | "typescript": "catalog:",
50 | "vitest": "catalog:"
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Realease
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | concurrency: ${{ github.workflow }}-${{ github.ref }}
9 |
10 | jobs:
11 | release:
12 | runs-on: ubuntu-latest
13 | steps:
14 | - name: Checkout repository
15 | uses: actions/checkout@v4
16 |
17 | - name: Set up Node.js
18 | uses: actions/setup-node@v4
19 | with:
20 | node-version: '18'
21 |
22 | - name: Cache pnpm modules
23 | uses: actions/cache@v4
24 | with:
25 | path: |
26 | **/node_modules
27 | **/.pnpm-store
28 | key: pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
29 | restore-keys: |
30 | pnpm-
31 |
32 | - name: Install pnpm
33 | run: npm install -g pnpm
34 |
35 | - name: Install dependencies
36 | run: pnpm install --frozen-lockfile
37 |
38 | - name: Build stepperize packages
39 | run: pnpm turbo run build --filter=@stepperize/react --filter=@stepperize/vue
40 |
41 | - name: Create Release Pull Request or Publish to npm
42 | uses: changesets/action@v1
43 | with:
44 | version: pnpm ci:version
45 | commit: "chore: new release"
46 | title: "chore: new release"
47 | publish: pnpm ci:release
48 | env:
49 | GITHUB_TOKEN: ${{ secrets.GH_ACTION_TOKEN }}
50 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
51 |
--------------------------------------------------------------------------------
/packages/solid/src/types.ts:
--------------------------------------------------------------------------------
1 | import type { Get, Metadata, Step, Stepper, Utils } from "@stepperize/core";
2 |
3 | export type ScopedProps = {
4 | /** The initial step to display. */
5 | initialStep?: Get.Id;
6 | /** The initial metadata. */
7 | initialMetadata?: Record, Metadata>;
8 | };
9 |
10 | export type StepperReturn = {
11 | /** The steps of the stepper. */
12 | steps: Steps;
13 | /**
14 | * `utils` provides helper functions to interact with steps in the stepper.
15 | * These functions allow you to get steps by their ID or index, get the first and last steps,
16 | * and navigate through the steps by retrieving neighbors or adjacent steps.
17 | *
18 | * @returns An object containing utility methods to interact with the steps
19 | */
20 | utils: Utils;
21 | /**
22 | * `useStepper` hook returns an object that manages the current step in the stepper.
23 | * You can use this hook to get the current step, navigate to the next or previous step,
24 | * and reset the stepper to the initial state.
25 | *
26 | * @param initialStep - The ID of the step to start with (optional).
27 | * @param initialMetadata - The initial metadata (optional).
28 | * @returns An object containing properties and methods to interact with the stepper.
29 | */
30 | useStepper: (props?: {
31 | initialStep?: Get.Id;
32 | initialMetadata?: Partial, Metadata>>;
33 | }) => Stepper;
34 | };
35 |
--------------------------------------------------------------------------------
/apps/docs/components/highlighter-code.tsx:
--------------------------------------------------------------------------------
1 | import { transformerTwoslash } from "fumadocs-twoslash";
2 | import { Popup, PopupContent, PopupTrigger } from "fumadocs-twoslash/ui";
3 | import { CodeBlock, Pre } from "fumadocs-ui/components/codeblock";
4 | import { toJsxRuntime } from "hast-util-to-jsx-runtime";
5 | import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6 | import { bundledLanguages, getSingletonHighlighter } from "shiki";
7 |
8 | export async function Code({
9 | code,
10 | lang = "ts",
11 | }: {
12 | lang?: string;
13 | code: string;
14 | }) {
15 | const highlighter = await getSingletonHighlighter({
16 | langs: Object.keys(bundledLanguages),
17 | themes: ["github-light-high-contrast", "github-dark-high-contrast"],
18 | });
19 |
20 | const hast = highlighter.codeToHast(code, {
21 | lang,
22 | themes: {
23 | light: "github-light-high-contrast",
24 | dark: "github-dark-high-contrast",
25 | },
26 | defaultColor: false,
27 | transformers: [
28 | transformerTwoslash({
29 | explicitTrigger: false,
30 | }),
31 | ],
32 | });
33 |
34 | const rendered = toJsxRuntime(hast, {
35 | Fragment,
36 | jsx,
37 | jsxs,
38 | development: false,
39 | components: {
40 | pre: (props) => (
41 |
42 | {props.children}
43 |
44 | ),
45 | Popup,
46 | PopupContent,
47 | PopupTrigger,
48 | },
49 | });
50 | return rendered;
51 | }
52 |
--------------------------------------------------------------------------------
/apps/docs/content/docs/vue/index.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Introduction
3 | description: A powerful library for creating flexible Stepper components in Vue
4 | icon: Album
5 | ---
6 |
7 | # Simplifying Multi-Step Processes in Vue
8 |
9 | Stepperize is a versatile Vue library designed to streamline the creation of Stepper components. It empowers developers to build complex, multi-step processes with ease, allowing them to focus on business logic rather than implementation details.
10 |
11 | ## Key Features
12 |
13 | - **Flexible Step Management**: Create linear or branching flows
14 | - **Headless Component**: Full control over styling and UI
15 | - **Conditional Rendering**: Dynamically adjust content based on user input
16 | - **State Management**: Built-in tools for handling step state
17 |
18 | ## Core Concepts
19 |
20 | ### Thinking in Steps
21 |
22 | Steppers break down complex workflows into manageable chunks, providing:
23 |
24 | - Clear navigation between steps
25 | - Visual progress indicators
26 | - Improved user experience for multi-stage processes
27 |
28 | **Common Use Cases**
29 |
30 | - User onboarding
31 | - Multi-page forms
32 | - E-commerce checkout
33 | - Product creation wizards
34 | - Tutorial walkthroughs
35 | - Surveys and questionnaires
36 | - Booking systems
37 |
38 | ### Thinking in Flows
39 |
40 | Stepperize goes beyond linear progressions, enabling:
41 |
42 | - Conditional rendering
43 | - Branching paths
44 | - Dynamic form adjustments
45 | - Personalized user experiences
46 | - State-dependent UI changes
47 | - Multi-path processes
48 |
--------------------------------------------------------------------------------
/apps/docs/content/docs/solid/index.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Introduction
3 | description: A powerful library for creating flexible Stepper components in Solid JS
4 | icon: Album
5 | ---
6 |
7 | # Simplifying Multi-Step Processes in Solid JS
8 |
9 | Stepperize is a versatile Solid JS library designed to streamline the creation of Stepper components. It empowers developers to build complex, multi-step processes with ease, allowing them to focus on business logic rather than implementation details.
10 |
11 | ## Key Features
12 |
13 | - **Flexible Step Management**: Create linear or branching flows
14 | - **Headless Component**: Full control over styling and UI
15 | - **Conditional Rendering**: Dynamically adjust content based on user input
16 | - **State Management**: Built-in tools for handling step state
17 |
18 | ## Core Concepts
19 |
20 | ### Thinking in Steps
21 |
22 | Steppers break down complex workflows into manageable chunks, providing:
23 |
24 | - Clear navigation between steps
25 | - Visual progress indicators
26 | - Improved user experience for multi-stage processes
27 |
28 | **Common Use Cases**
29 |
30 | - User onboarding
31 | - Multi-page forms
32 | - E-commerce checkout
33 | - Product creation wizards
34 | - Tutorial walkthroughs
35 | - Surveys and questionnaires
36 | - Booking systems
37 |
38 | ### Thinking in Flows
39 |
40 | Stepperize goes beyond linear progressions, enabling:
41 |
42 | - Conditional rendering
43 | - Branching paths
44 | - Dynamic form adjustments
45 | - Personalized user experiences
46 | - State-dependent UI changes
47 | - Multi-path processes
48 |
--------------------------------------------------------------------------------
/apps/docs/content/docs/solid/api-references/define.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Define
3 | description: How to define a stepper using defineStepper
4 | ---
5 |
6 | `defineStepper` is the core function for creating a customizable stepper. It generates the necessary hooks and utilities for building your stepper UI and managing its state.
7 |
8 | ## Usage
9 |
10 |
11 |
12 | ### Import
13 |
14 | ```tsx twoslash
15 | import { defineStepper } from "@stepperize/solid";
16 | ```
17 |
18 |
19 |
20 | ### Define steps
21 | Call `defineStepper` with your step configurations:
22 |
23 | ```tsx twoslash
24 | import { defineStepper } from "@stepperize/solid"
25 | // ---cut-before---
26 | const { useStepper, steps } = defineStepper(
27 | { id: "step-1", title: "Step 1", description: "First step" },
28 | { id: "step-2", title: "Step 2", description: "Second step" },
29 | { id: "step-3", title: "Step 3", description: "Third step" }
30 | );
31 | ```
32 |
33 | - `id` (required): Unique identifier for each step
34 | - Add any custom properties (e.g., `title`, `description`) for use in your UI
35 |
36 |
37 |
38 |
39 | ## Return Value
40 |
41 | `defineStepper` returns an object with:
42 |
43 | - `useStepper`: A custom hook for accessing and controlling the stepper
44 | - `steps`: An array of the defined step objects
45 | - `utils`: Pure utility functions for working with steps
46 |
47 | ## Next Steps
48 |
49 | Learn how to use the returned components and hooks:
50 |
51 | - [useStepper Hook](./hook)
52 |
--------------------------------------------------------------------------------
/apps/docs/content/docs/react/migration/migrating-to-v3.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Migrating to v3/v4
3 | description: Learn how to migrate from v2 to v3 and v4
4 | icon: CircleFadingArrowUp
5 | ---
6 |
7 | Stepperize v3 brings with it a number of naming changes to make the pattern.
8 | It also adds a `switch` method that allows to render in a simpler way the components we want for each step.
9 |
10 | ## Breaking Changes
11 |
12 | ### Name changes in the values returned by the hook
13 |
14 | The values returned by the `useStepper` hook have been renamed to make the pattern more consistent.
15 |
16 | - `currentStep` is now `current`.
17 | - `isFirstStep` is now `isFirst`.
18 | - `isLastStep` is now `isLast`.
19 | - `getStepById` is now `get`.
20 | - `goToNextStep` is now `next`.
21 | - `goToPrevStep` is now `prev`.
22 | - `goToStep` is now `goTo`.
23 |
24 | ## New Features
25 |
26 | ### New `switch` method
27 |
28 | The `switch` method has been added to the `useStepper` hook. This method allows us to render the components we want for each step in a simpler way.
29 |
30 | ```tsx
31 | const MySteps = () => {
32 | const stepper = useStepper();
33 |
34 | return (
35 | <>
36 | {stepper.switch({
37 | first: (step) => This is the {step.title} step.
,
38 | second: (step) => This is the {step.title} step.
,
39 | last: (step) => You have reached the {step.title} step.
,
40 | })}
41 | >
42 | );
43 | };
44 | ```
45 |
46 | ### Add `all` property
47 |
48 | The `all` property has been added to the object returned by the `useStepper` hook. This property contains all the steps in the stepper.
49 |
--------------------------------------------------------------------------------
/packages/svelte/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @stepperize/svelte
2 |
3 | ## 0.1.5
4 |
5 | ### Patch Changes
6 |
7 | - [#140](https://github.com/damianricobelli/stepperize/pull/140) [`df6fe81`](https://github.com/damianricobelli/stepperize/commit/df6fe81274ce618cb28469c23db73a8f376b542a) Thanks [@damianricobelli](https://github.com/damianricobelli)! - chore: update package versions and fix icon component props
8 |
9 | ## 0.1.4
10 |
11 | ### Patch Changes
12 |
13 | - [`5fdf934`](https://github.com/damianricobelli/stepperize/commit/5fdf9344f38a8673220adbc57f8ea55489883563) Thanks [@damianricobelli](https://github.com/damianricobelli)! - Add main entry point
14 |
15 | ## 0.1.3
16 |
17 | ### Patch Changes
18 |
19 | - [#119](https://github.com/damianricobelli/stepperize/pull/119) [`921bb62`](https://github.com/damianricobelli/stepperize/commit/921bb6297a0f370fbe5cbf4689b5a698207ee62c) Thanks [@longzheng](https://github.com/longzheng)! - Update package.json `types` to match filename
20 |
21 | ## 0.1.2
22 |
23 | ### Patch Changes
24 |
25 | - [#85](https://github.com/damianricobelli/stepperize/pull/85) [`752f9a6`](https://github.com/damianricobelli/stepperize/commit/752f9a6907cc5e7e623a66350c82eeba9559fea7) Thanks [@damianricobelli](https://github.com/damianricobelli)! - chore: lint and fix ci
26 |
27 | ## 0.1.1
28 |
29 | ### Patch Changes
30 |
31 | - [#80](https://github.com/damianricobelli/stepperize/pull/80) [`e0f5de7`](https://github.com/damianricobelli/stepperize/commit/e0f5de733f9f42527e62cdb35f8e6ca42063b187) Thanks [@damianricobelli](https://github.com/damianricobelli)! - chore: update all packages deps and upgrade docs to latest fumadocs version
32 |
--------------------------------------------------------------------------------
/apps/docs/content/docs/react/index.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Introduction
3 | description: A powerful library for creating flexible Stepper components in React
4 | icon: Album
5 | ---
6 |
7 | # Simplifying Multi-Step Processes in React
8 |
9 | Stepperize is a versatile React library designed to streamline the creation of Stepper components. It empowers developers to build complex, multi-step processes with ease, allowing them to focus on business logic rather than implementation details.
10 |
11 | ## Key Features
12 |
13 | - **Flexible Step Management**: Create linear or branching flows
14 | - **Headless Component**: Full control over styling and UI
15 | - **React Native Compatible**: Build steppers for web and mobile apps
16 | - **Conditional Rendering**: Dynamically adjust content based on user input
17 | - **State Management**: Built-in tools for handling step state
18 |
19 | ## Core Concepts
20 |
21 | ### Thinking in Steps
22 |
23 | Steppers break down complex workflows into manageable chunks, providing:
24 |
25 | - Clear navigation between steps
26 | - Visual progress indicators
27 | - Improved user experience for multi-stage processes
28 |
29 | **Common Use Cases**
30 |
31 | - User onboarding
32 | - Multi-page forms
33 | - E-commerce checkout
34 | - Product creation wizards
35 | - Tutorial walkthroughs
36 | - Surveys and questionnaires
37 | - Booking systems
38 |
39 | ### Thinking in Flows
40 |
41 | Stepperize goes beyond linear progressions, enabling:
42 |
43 | - Conditional rendering
44 | - Branching paths
45 | - Dynamic form adjustments
46 | - Personalized user experiences
47 | - State-dependent UI changes
48 | - Multi-path processes
49 |
--------------------------------------------------------------------------------
/apps/docs/app/layout.config.tsx:
--------------------------------------------------------------------------------
1 | import { icons } from "@/components/icons";
2 |
3 | import type { LinkItemType } from "fumadocs-ui/layouts/links";
4 | import type { BaseLayoutProps } from "fumadocs-ui/layouts/shared";
5 | import { Waypoints } from "lucide-react";
6 |
7 | export const linkItems: LinkItemType[] = [
8 | {
9 | icon: ,
10 | text: "React",
11 | url: "/docs/react",
12 | active: "nested-url",
13 | description: "Stepperize for React",
14 | },
15 | {
16 | icon: ,
17 | text: "Shadcn",
18 | url: "/docs/react/shadcn/introduction",
19 | active: "nested-url",
20 | description: "Stepperize for Shadcn",
21 | },
22 | {
23 | icon: ,
24 | text: "Vue",
25 | url: "/docs/vue",
26 | active: "nested-url",
27 | description: "Stepperize for Vue",
28 | },
29 | {
30 | icon: ,
31 | text: "Solid",
32 | url: "/docs/solid",
33 | active: "nested-url",
34 | description: "Stepperize for Solid",
35 | },
36 | {
37 | icon: ,
38 | text: "Svelte",
39 | url: "/docs/svelte",
40 | active: "nested-url",
41 | description: "Stepperize for Svelte",
42 | },
43 | ];
44 |
45 | export const baseOptions: BaseLayoutProps = {
46 | githubUrl: "https://github.com/damianricobelli/stepperize",
47 | nav: {
48 | title: (
49 |
50 |
51 | Stepperize
52 |
53 | ),
54 | transparentMode: "top",
55 | },
56 | links: [...linkItems],
57 | };
58 |
--------------------------------------------------------------------------------
/apps/docs/registry/new-york/ui/radio-group.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
4 | import { CircleIcon } from "lucide-react";
5 | import type * as React from "react";
6 |
7 | import { cn } from "@/lib/utils";
8 |
9 | function RadioGroup({
10 | className,
11 | ...props
12 | }: React.ComponentProps) {
13 | return (
14 |
19 | );
20 | }
21 |
22 | function RadioGroupItem({
23 | className,
24 | ...props
25 | }: React.ComponentProps) {
26 | return (
27 |
35 |
39 |
40 |
41 |
42 | );
43 | }
44 |
45 | export { RadioGroup, RadioGroupItem };
46 |
--------------------------------------------------------------------------------
/apps/docs/content/docs/react/api-references/define.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Define
3 | description: How to define a stepper using defineStepper
4 | ---
5 |
6 | `defineStepper` is the core function for creating a customizable stepper. It generates the necessary components and hooks for building your stepper UI and managing its state.
7 |
8 | ## Usage
9 |
10 |
11 |
12 | ### Import
13 |
14 | ```tsx twoslash
15 | import { defineStepper } from "@stepperize/react";
16 | ```
17 |
18 |
19 |
20 | ### Define steps
21 | Call `defineStepper` with your step configurations:
22 |
23 | ```tsx twoslash
24 | import { defineStepper } from "@stepperize/react"
25 | // ---cut-before---
26 | const { Scoped, useStepper, steps } = defineStepper(
27 | { id: "step-1", title: "Step 1", description: "First step" },
28 | { id: "step-2", title: "Step 2", description: "Second step" },
29 | { id: "step-3", title: "Step 3", description: "Third step" }
30 | );
31 | ```
32 |
33 | - `id` (required): Unique identifier for each step
34 | - Add any custom properties (e.g., `title`, `description`) for use in your UI
35 |
36 |
37 |
38 |
39 | ## Return Value
40 |
41 | `defineStepper` returns an object with:
42 |
43 | - `Scoped`: A Provider component for scoping stepper state
44 | - `useStepper`: A custom hook for accessing and controlling the stepper
45 | - `steps`: An array of the defined step objects
46 | - `utils`: Pure utility functions for working with steps
47 |
48 | ## Next Steps
49 |
50 | Learn how to use the returned components and hooks:
51 |
52 | - [useStepper Hook](./hook)
53 | - [Scoped Component](./scoped)
54 |
--------------------------------------------------------------------------------
/apps/docs/content/docs/vue/api-references/define.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Define
3 | description: How to define a stepper using defineStepper
4 | ---
5 |
6 | `defineStepper` is the core function for creating a customizable stepper. It generates the necessary components and composables for building your stepper UI and managing its state.
7 |
8 | ## Usage
9 |
10 |
11 |
12 | ### Import
13 |
14 | ```tsx twoslash
15 | import { defineStepper } from "@stepperize/vue";
16 | ```
17 |
18 |
19 |
20 | ### Define steps
21 |
22 | Call `defineStepper` with your step configurations:
23 |
24 | ```tsx twoslash
25 | import { defineStepper } from "@stepperize/vue";
26 |
27 | const { Scoped, useStepper, steps } = defineStepper(
28 | { id: "step-1", title: "Step 1", description: "First step" },
29 | { id: "step-2", title: "Step 2", description: "Second step" },
30 | { id: "step-3", title: "Step 3", description: "Third step" }
31 | );
32 | ```
33 |
34 | - `id` (required): Unique identifier for each step
35 | - Add any custom properties (e.g., `title`, `description`) for use in your UI
36 |
37 |
38 |
39 |
40 | ## Return Value
41 |
42 | `defineStepper` returns an object with:
43 |
44 | - `Scoped`: A Provider component for scoping stepper state
45 | - `useStepper`: A custom composable for accessing and controlling the stepper
46 | - `steps`: An array of the defined step objects
47 | - `utils`: Pure utility functions for working with steps
48 |
49 | ## Next Steps
50 |
51 | Learn how to use the returned components and composables:
52 |
53 | - [useStepper Composable](./composable)
54 | - [Scoped Component](./scoped)
55 |
--------------------------------------------------------------------------------
/apps/docs/components/card.tsx:
--------------------------------------------------------------------------------
1 | import Link from 'fumadocs-core/link';
2 | import type { HTMLAttributes, ReactNode } from 'react';
3 | import { cn } from '../lib/cn';
4 |
5 | export function Cards(
6 | props: HTMLAttributes,
7 | ): React.ReactElement {
8 | return (
9 |
13 | {props.children}
14 |
15 | );
16 | }
17 |
18 | export type CardProps = HTMLAttributes & {
19 | icon?: ReactNode;
20 | title: ReactNode;
21 | description?: ReactNode;
22 |
23 | href?: string;
24 | external?: boolean;
25 | };
26 |
27 | export function Card({
28 | icon,
29 | title,
30 | description,
31 | ...props
32 | }: CardProps): React.ReactElement {
33 | const E = props.href ? Link : 'div';
34 |
35 | return (
36 |
45 | {icon ? (
46 |
47 | {icon}
48 |
49 | ) : null}
50 | {title}
51 | {description ? (
52 | {description}
53 | ) : null}
54 | {props.children ? (
55 |
56 | {props.children}
57 |
58 | ) : null}
59 |
60 | );
61 | }
62 |
--------------------------------------------------------------------------------
/apps/docs/app/(home)/page.tsx:
--------------------------------------------------------------------------------
1 | import { Code } from "@/components/highlighter-code";
2 | import { CodeExample } from "@/components/home/code-example";
3 | import { Cta } from "@/components/home/cta";
4 | import { DemoSection } from "@/components/home/demo-section";
5 | import Features from "@/components/home/features";
6 | import { Footer } from "@/components/home/footer";
7 | import Hero from "@/components/home/hero";
8 |
9 | export default function HomePage() {
10 | return (
11 |
12 |
13 |
14 |
15 |
16 | {
26 | const methods = stepper.useStepper();
27 |
28 | return (
29 |
30 |
31 | methods.next()}>Next
32 | methods.prev()}>Prev
33 |
34 | {methods.switch({
35 | "step-1": (step) =>
First: {step.title} ,
36 | "step-2": (step) => (
37 |
38 |
Second: {step.title}
39 |
{step.description}
40 |
41 | )
42 | })}
43 |
44 | );
45 | }
46 | `}
47 | lang="tsx"
48 | />
49 |
50 |
51 |
52 |
53 |
54 |
55 | );
56 | }
57 |
--------------------------------------------------------------------------------
/apps/docs/app/docs/layout.tsx:
--------------------------------------------------------------------------------
1 | import { baseOptions } from "@/app/layout.config";
2 | import { icons } from "@/components/icons";
3 | import { source } from "@/lib/source";
4 | import { RootToggle } from "fumadocs-ui/components/layout/root-toggle";
5 | import { DocsLayout } from "fumadocs-ui/layouts/docs";
6 | import type { ReactNode } from "react";
7 |
8 | interface Mode {
9 | param: string;
10 | name: string;
11 | description?: string;
12 | icon: React.FC>;
13 | }
14 |
15 | const modes: Mode[] = [
16 | {
17 | param: "react",
18 | name: "React",
19 | description: "Stepperize for React",
20 | icon: icons.ReactIcon,
21 | },
22 | {
23 | param: "vue",
24 | name: "Vue",
25 | description: "Stepperize for Vue",
26 | icon: icons.VueIcon,
27 | },
28 | {
29 | param: "solid",
30 | name: "Solid",
31 | description: "Stepperize for Solid",
32 | icon: icons.SolidIcon,
33 | },
34 | {
35 | param: "svelte",
36 | name: "Svelte",
37 | description: "Stepperize for Svelte",
38 | icon: icons.SvelteIcon,
39 | },
40 | ];
41 |
42 | export default function Layout({ children }: { children: ReactNode }) {
43 | return (
44 | ({
54 | url: `/docs/${mode.param}`,
55 | icon: ,
56 | title: mode.name,
57 | description: mode.description,
58 | }))}
59 | />
60 | ),
61 | }}
62 | >
63 | {children}
64 |
65 | );
66 | }
67 |
--------------------------------------------------------------------------------
/apps/docs/content/docs/react/examples/basic.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Basic Examples
3 | description: Explore various usage patterns of Stepperize in React applications.
4 | ---
5 |
6 | ## Horizontal Stepper
7 |
8 | The horizontal stepper is the most common and straightforward implementation of Stepperize. It provides a linear progression through a series of steps, ideal for workflows or multi-step forms.
9 |
10 | Key features:
11 |
12 | - Horizontal layout
13 | - Simple navigation using the `switch` method
14 |
15 | ### Live Demo
16 |
17 |
18 |
19 | ## Vertical Stepper
20 |
21 | For interfaces with limited horizontal space or when you need to display more detailed information for each step, the vertical stepper is an excellent choice.
22 |
23 | Key features:
24 |
25 | - Vertical layout
26 | - Suitable for mobile interfaces or sidebars
27 |
28 | ### Live Demo
29 |
30 |
34 |
35 | ## Custom Circular Stepper
36 |
37 | Stepperize allows for extensive customization. This example showcases a circular stepper design, demonstrating how you can tailor the appearance to match your application's aesthetic.
38 |
39 | Key features:
40 |
41 | - Custom circular step indicators
42 | - Flexible layout options
43 |
44 | ### Live Demo
45 |
46 |
47 |
48 | ## Usage Tips
49 |
50 | - Use the `switch` method to programmatically change steps
51 | - Customize the appearance using CSS, Tailwind CSS, or any other styling library
52 | - Combine with form libraries for complex multi-step forms
53 | - Implement validation logic between steps for guided user experiences
54 |
--------------------------------------------------------------------------------
/apps/docs/components/callout.tsx:
--------------------------------------------------------------------------------
1 | import { AlertTriangle, CircleX, Info } from 'lucide-react';
2 | import { forwardRef, type HTMLAttributes, type ReactNode } from 'react';
3 | import { cn } from '../lib/cn';
4 | import { cva } from 'class-variance-authority';
5 |
6 | type CalloutProps = Omit<
7 | HTMLAttributes,
8 | 'title' | 'type' | 'icon'
9 | > & {
10 | title?: ReactNode;
11 | /**
12 | * @defaultValue info
13 | */
14 | type?: 'info' | 'warn' | 'error';
15 |
16 | /**
17 | * Force an icon
18 | */
19 | icon?: ReactNode;
20 | };
21 |
22 | const calloutVariants = cva(
23 | 'my-6 flex flex-row gap-2 rounded-lg border border-s-2 bg-fd-card p-3 text-sm text-fd-card-foreground shadow-md',
24 | {
25 | variants: {
26 | type: {
27 | info: 'border-s-blue-500/50',
28 | warn: 'border-s-orange-500/50',
29 | error: 'border-s-red-500/50',
30 | },
31 | },
32 | },
33 | );
34 |
35 | export const Callout = forwardRef(
36 | ({ className, children, title, type = 'info', icon, ...props }, ref) => {
37 | return (
38 |
48 | {icon ??
49 | {
50 | info:
,
51 | warn: (
52 |
53 | ),
54 | error:
,
55 | }[type]}
56 |
57 | {title ?
{title}
: null}
58 |
59 | {children}
60 |
61 |
62 |
63 | );
64 | },
65 | );
66 |
67 | Callout.displayName = 'Callout';
68 |
--------------------------------------------------------------------------------
/apps/docs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "docs",
3 | "version": "2.5.7",
4 | "private": true,
5 | "scripts": {
6 | "build": "next build --turbo",
7 | "dev": "next dev --turbo",
8 | "start": "next start",
9 | "postinstall": "fumadocs-mdx",
10 | "registry:build": "shadcn build",
11 | "clean": "rm -rf .turbo .next node_modules"
12 | },
13 | "dependencies": {
14 | "@hookform/resolvers": "^5.2.2",
15 | "@radix-ui/colors": "^3.0.0",
16 | "@radix-ui/react-accordion": "^1.2.12",
17 | "@radix-ui/react-collapsible": "^1.1.12",
18 | "@radix-ui/react-label": "^2.1.7",
19 | "@radix-ui/react-popover": "^1.1.15",
20 | "@radix-ui/react-radio-group": "^1.3.8",
21 | "@radix-ui/react-scroll-area": "^1.2.10",
22 | "@radix-ui/react-slot": "^1.2.3",
23 | "@radix-ui/react-tabs": "^1.1.13",
24 | "@stepperize/react": "workspace:*",
25 | "@stepperize/solid": "workspace:*",
26 | "@stepperize/vue": "workspace:*",
27 | "arktype": "^2.1.22",
28 | "class-variance-authority": "^0.7.1",
29 | "clsx": "^2.1.1",
30 | "fumadocs-core": "15.8.3",
31 | "fumadocs-mdx": "12.0.2",
32 | "fumadocs-twoslash": "3.1.8",
33 | "fumadocs-typescript": "4.0.11",
34 | "fumadocs-ui": "15.8.3",
35 | "geist": "1.5.1",
36 | "hast-util-to-jsx-runtime": "^2.3.6",
37 | "lucide-react": "^0.544.0",
38 | "motion": "^12.23.22",
39 | "next": "15.5.4",
40 | "react": "catalog:",
41 | "react-dom": "catalog:",
42 | "react-hook-form": "^7.64.0",
43 | "shadcn": "3.4.0",
44 | "shiki": "3.13.0",
45 | "solid-js": "^1.9.9",
46 | "tailwind-merge": "^3.3.1",
47 | "ts-morph": "27.0.0",
48 | "tw-animate-css": "^1.4.0",
49 | "twoslash": "^0.3.4",
50 | "valibot": "^1.1.0",
51 | "zod": "^4.1.11"
52 | },
53 | "devDependencies": {
54 | "@fumadocs/cli": "^1.0.2",
55 | "@tailwindcss/postcss": "^4.1.14",
56 | "@types/mdx": "^2.0.13",
57 | "@types/node": "24.7.0",
58 | "@types/react": "catalog:",
59 | "@types/react-dom": "catalog:",
60 | "@types/react-syntax-highlighter": "^15.5.13",
61 | "postcss": "^8.5.6",
62 | "tailwindcss": "^4.1.14",
63 | "typescript": "catalog:"
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/apps/docs/components/open-in-v0.tsx:
--------------------------------------------------------------------------------
1 | import { Button } from "@/registry/new-york/ui/button";
2 |
3 | export const OpenInV0 = ({
4 | name,
5 | children,
6 | }: {
7 | name: string;
8 | children: React.ReactNode;
9 | }) => {
10 | return (
11 |
12 |
13 |
14 | {children}
15 |
16 |
17 | );
18 | };
19 |
20 | function OpenInV0Button({ name }: { name: string }) {
21 | return (
22 |
27 |
32 | Open in{" "}
33 |
39 | Open in v0
40 |
44 |
48 |
49 |
50 |
51 | );
52 | }
53 |
--------------------------------------------------------------------------------
/packages/vue/src/components.ts:
--------------------------------------------------------------------------------
1 | import type { Get, Step, Stepper } from "@stepperize/core";
2 | import { type EmitsOptions, type SetupContext, type SlotsType, defineComponent } from "vue";
3 |
4 | export interface StepperWhenProps> {
5 | stepper: Stepper;
6 | when: Id | [Id, ...boolean[]];
7 | }
8 |
9 | export const StepperWhen = defineComponent(
10 | >(
11 | props: StepperWhenProps,
12 | {
13 | slots,
14 | }: SetupContext<
15 | EmitsOptions,
16 | SlotsType<{
17 | default?: (step: Get.StepById) => any;
18 | else?: (step: Get.StepSansId) => any;
19 | }>
20 | >,
21 | ) => {
22 | return () =>
23 | props.stepper.when(
24 | props.when,
25 | (step) => slots.default?.(step),
26 | (step) => slots.else?.(step),
27 | );
28 | },
29 | {
30 | name: "StepperWhen",
31 | props: ["stepper", "when"] as any, // fix vue typing issue,
32 | },
33 | );
34 |
35 | export interface StepperSwitchProps {
36 | stepper: Stepper;
37 | }
38 |
39 | export const StepperSwitch = defineComponent(
40 | (
41 | props: StepperSwitchProps,
42 | { slots }: SetupContext>>,
43 | ) => {
44 | return () => props.stepper.switch(slots as Get.Switch);
45 | },
46 | {
47 | name: "StepperSwitch",
48 | props: ["stepper"] as any, // fix vue typing issue,
49 | },
50 | );
51 |
52 | export interface StepperMatchProps, Steps extends Step[]> {
53 | stepper: Stepper;
54 | state: State;
55 | }
56 |
57 | export const StepperMatch = defineComponent(
58 | , const Steps extends Step[]>(
59 | props: StepperMatchProps,
60 | { slots }: SetupContext>>,
61 | ) => {
62 | return () => props.stepper.match(props.state, slots as Get.Switch);
63 | },
64 | {
65 | name: "StepperMatch",
66 | props: ["stepper", "state"] as any, // fix vue typing issue,
67 | },
68 | );
69 |
--------------------------------------------------------------------------------
/apps/docs/components/ui/scroll-area.tsx:
--------------------------------------------------------------------------------
1 | import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';
2 | import * as React from 'react';
3 | import { cn } from '../../lib/cn';
4 |
5 | const ScrollArea = React.forwardRef<
6 | React.ComponentRef,
7 | React.ComponentPropsWithoutRef
8 | >(({ className, children, ...props }, ref) => (
9 |
14 | {children}
15 |
16 |
17 |
18 | ));
19 |
20 | ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
21 |
22 | const ScrollViewport = React.forwardRef<
23 | React.ComponentRef,
24 | React.ComponentPropsWithoutRef
25 | >(({ className, children, ...props }, ref) => (
26 |
31 | {children}
32 |
33 | ));
34 |
35 | ScrollViewport.displayName = ScrollAreaPrimitive.Viewport.displayName;
36 |
37 | const ScrollBar = React.forwardRef<
38 | React.ComponentRef,
39 | React.ComponentPropsWithoutRef
40 | >(({ className, orientation = 'vertical', ...props }, ref) => (
41 |
52 |
53 |
54 | ));
55 | ScrollBar.displayName = ScrollAreaPrimitive.Scrollbar.displayName;
56 |
57 | export { ScrollArea, ScrollBar, ScrollViewport };
58 |
--------------------------------------------------------------------------------
/apps/docs/registry/new-york/ui/button.tsx:
--------------------------------------------------------------------------------
1 | import { Slot } from "@radix-ui/react-slot";
2 | import { type VariantProps, cva } from "class-variance-authority";
3 | import * as React from "react";
4 |
5 | import { cn } from "@/lib/utils";
6 |
7 | const buttonVariants = cva(
8 | "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
9 | {
10 | variants: {
11 | variant: {
12 | default:
13 | "bg-primary text-primary-foreground shadow-sm hover:bg-primary/90",
14 | destructive:
15 | "bg-destructive text-destructive-foreground shadow-xs hover:bg-destructive/90",
16 | outline:
17 | "border border-input bg-background shadow-xs hover:bg-accent hover:text-accent-foreground",
18 | secondary:
19 | "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
20 | ghost: "hover:bg-accent hover:text-accent-foreground",
21 | link: "text-primary underline-offset-4 hover:underline",
22 | },
23 | size: {
24 | default: "h-9 px-4 py-2",
25 | sm: "h-8 rounded-md px-3 text-xs",
26 | lg: "h-10 rounded-md px-8",
27 | icon: "h-9 w-9",
28 | },
29 | },
30 | defaultVariants: {
31 | variant: "default",
32 | size: "default",
33 | },
34 | }
35 | );
36 |
37 | export interface ButtonProps
38 | extends React.ButtonHTMLAttributes,
39 | VariantProps {
40 | asChild?: boolean;
41 | }
42 |
43 | const Button = React.forwardRef(
44 | ({ className, variant, size, asChild = false, ...props }, ref) => {
45 | const Comp = asChild ? Slot : "button";
46 | return (
47 |
52 | );
53 | }
54 | );
55 | Button.displayName = "Button";
56 |
57 | export { Button, buttonVariants };
58 |
--------------------------------------------------------------------------------
/packages/react/src/types.ts:
--------------------------------------------------------------------------------
1 | import type { Get, Metadata, Step, Stepper, Utils } from "@stepperize/core";
2 |
3 | export type ScopedProps = React.PropsWithChildren<{
4 | /** The initial step to display. */
5 | initialStep?: Get.Id;
6 | /** The initial metadata. */
7 | initialMetadata?: Record, Metadata>;
8 | }>;
9 |
10 | export type StepperReturn = {
11 | /** The steps of the stepper. */
12 | steps: Steps;
13 | /**
14 | * `utils` provides helper functions to interact with steps in the stepper.
15 | * These functions allow you to get steps by their ID or index, get the first and last steps,
16 | * and navigate through the steps by retrieving neighbors or adjacent steps.
17 | *
18 | * @returns An object containing utility methods to interact with the steps
19 | */
20 | utils: Utils;
21 | /**
22 | * `Scoped` component is a wrapper that provides the stepper context to its children.
23 | * It uses the `Context` to pass the stepper instance to the children.
24 | *
25 | * @param props - The props object containing `initialStep` and `children`.
26 | * @param props.initialStep - The ID of the step to start with (optional).
27 | * @param props.initialMetadata - The initial metadata (optional).
28 | * @param props.children - The child elements to be wrapped by the `Scoped` component.
29 | * @returns A React element that wraps the children with the stepper context.
30 | */
31 | Scoped: (props: ScopedProps) => React.ReactElement;
32 | /**
33 | * `useStepper` hook returns an object that manages the current step in the stepper.
34 | * You can use this hook to get the current step, navigate to the next or previous step,
35 | * and reset the stepper to the initial state.
36 | *
37 | * @param initialStep - The ID of the step to start with (optional).
38 | * @param initialMetadata - The initial metadata (optional).
39 | * @returns An object containing properties and methods to interact with the stepper.
40 | */
41 | useStepper: (props?: {
42 | initialStep?: Get.Id;
43 | initialMetadata?: Partial, Metadata>>;
44 | }) => Stepper;
45 | };
46 |
--------------------------------------------------------------------------------
/packages/vue/src/types.ts:
--------------------------------------------------------------------------------
1 | import type { Get, Metadata, Step, Stepper, Utils } from "@stepperize/core";
2 | import type { Component, ComputedRef, MaybeRefOrGetter } from "vue";
3 |
4 | export type ScopedProps = {
5 | /** The initial step to display. */
6 | initialStep?: Get.Id;
7 | };
8 |
9 | export type StepperReturn = {
10 | /** The steps of the stepper. */
11 | steps: Steps;
12 | /**
13 | * `utils` provides helper functions to interact with steps in the stepper.
14 | * These functions allow you to get steps by their ID or index, get the first and last steps,
15 | * and navigate through the steps by retrieving neighbors or adjacent steps.
16 | *
17 | * @returns An object containing utility methods to interact with the steps
18 | */
19 | utils: Utils;
20 | /**
21 | * `Scoped` component is a wrapper that provides the stepper context to its children.
22 | * It uses the `Context` to pass the stepper instance to the children.
23 | *
24 | * @param props - The props object containing `initialStep` and `children`.
25 | * @param props.initialStep - The ID of the step to start with (optional).
26 | * @param props.initialMetadata - The initial metadata (optional).
27 | * @param props.children - The child elements to be wrapped by the `Scoped` component.
28 | * @returns A Vue VNode that wraps the children with the stepper context.
29 | */
30 | Scoped: Component>;
31 | /**
32 | * `useStepper` composable returns an object that manages the current step in the stepper.
33 | * You can use this composable to get the current step, navigate to the next or previous step,
34 | * and reset the stepper to the initial state.
35 | *
36 | * @param initialStep - The ID of the step to start with (optional).
37 | * @param initialMetadata - The initial metadata (optional).
38 | * @returns An object containing properties and methods to interact with the stepper.
39 | */
40 | useStepper: (props?: {
41 | initialStep?: MaybeRefOrGetter>;
42 | initialMetadata?: Partial, Metadata>>;
43 | }) => ComputedRef>;
44 | };
45 |
--------------------------------------------------------------------------------
/apps/docs/components/ui/tabs.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 | import * as TabsPrimitive from '@radix-ui/react-tabs';
3 | import * as React from 'react';
4 | import { cn } from '../../lib/cn';
5 |
6 | const Tabs = React.forwardRef<
7 | React.ComponentRef,
8 | React.ComponentPropsWithoutRef
9 | >((props, ref) => {
10 | return (
11 |
19 | );
20 | });
21 |
22 | Tabs.displayName = 'Tabs';
23 |
24 | const TabsList = React.forwardRef<
25 | React.ComponentRef,
26 | React.ComponentPropsWithoutRef
27 | >((props, ref) => (
28 |
36 | ));
37 | TabsList.displayName = 'TabsList';
38 |
39 | const TabsTrigger = React.forwardRef<
40 | React.ComponentRef,
41 | React.ComponentPropsWithoutRef
42 | >((props, ref) => (
43 |
51 | ));
52 | TabsTrigger.displayName = 'TabsTrigger';
53 |
54 | const TabsContent = React.forwardRef<
55 | React.ComponentRef,
56 | React.ComponentPropsWithoutRef
57 | >((props, ref) => (
58 |
63 | ));
64 | TabsContent.displayName = 'TabsContent';
65 |
66 | export { Tabs, TabsList, TabsTrigger, TabsContent };
67 |
--------------------------------------------------------------------------------
/apps/docs/components/files.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import { cva } from 'class-variance-authority';
4 | import { FileIcon, FolderIcon, FolderOpen } from 'lucide-react';
5 | import { useState, type HTMLAttributes, type ReactNode } from 'react';
6 | import { cn } from '../lib/cn';
7 | import {
8 | Collapsible,
9 | CollapsibleContent,
10 | CollapsibleTrigger,
11 | } from './ui/collapsible';
12 |
13 | const itemVariants = cva(
14 | 'flex flex-row items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-fd-accent hover:text-fd-accent-foreground [&_svg]:size-4',
15 | );
16 |
17 | export function Files({
18 | className,
19 | ...props
20 | }: HTMLAttributes): React.ReactElement {
21 | return (
22 |
26 | {props.children}
27 |
28 | );
29 | }
30 |
31 | export interface FileProps extends HTMLAttributes {
32 | name: string;
33 | icon?: ReactNode;
34 | }
35 |
36 | export interface FolderProps extends HTMLAttributes {
37 | name: string;
38 |
39 | disabled?: boolean;
40 |
41 | /**
42 | * Open folder by default
43 | *
44 | * @defaultValue false
45 | */
46 | defaultOpen?: boolean;
47 | }
48 |
49 | export function File({
50 | name,
51 | icon = ,
52 | className,
53 | ...rest
54 | }: FileProps): React.ReactElement {
55 | return (
56 |
57 | {icon}
58 | {name}
59 |
60 | );
61 | }
62 |
63 | export function Folder({
64 | name,
65 | defaultOpen = false,
66 | ...props
67 | }: FolderProps): React.ReactElement {
68 | const [open, setOpen] = useState(defaultOpen);
69 |
70 | return (
71 |
72 |
73 | {open ? : }
74 | {name}
75 |
76 |
77 | {props.children}
78 |
79 |
80 | );
81 | }
82 |
--------------------------------------------------------------------------------
/apps/docs/components/layout/language-toggle.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 | import { type ButtonHTMLAttributes, type HTMLAttributes } from 'react';
3 | import { useI18n } from 'fumadocs-ui/provider';
4 | import {
5 | Popover,
6 | PopoverContent,
7 | PopoverTrigger,
8 | } from '../ui/popover';
9 | import { cn } from '../../lib/cn';
10 | import { buttonVariants } from '../ui/button';
11 |
12 | export type LanguageSelectProps = ButtonHTMLAttributes;
13 |
14 | export function LanguageToggle(props: LanguageSelectProps): React.ReactElement {
15 | const context = useI18n();
16 | if (!context.locales) throw new Error('Missing ` `');
17 |
18 | return (
19 |
20 |
31 | {props.children}
32 |
33 |
34 |
35 | {context.text.chooseLanguage}
36 |
37 | {context.locales.map((item) => (
38 | {
48 | context.onChange?.(item.locale);
49 | }}
50 | >
51 | {item.name}
52 |
53 | ))}
54 |
55 |
56 | );
57 | }
58 |
59 | export function LanguageToggleText(
60 | props: HTMLAttributes,
61 | ): React.ReactElement {
62 | const context = useI18n();
63 | const text = context.locales?.find(
64 | (item) => item.locale === context.locale,
65 | )?.name;
66 |
67 | return {text} ;
68 | }
69 |
--------------------------------------------------------------------------------
/apps/docs/registry/new-york/blocks/stepper-demo/components/stepper-demo.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as React from "react";
4 |
5 | import { defineStepper } from "@/registry/new-york/blocks/stepper-demo/components/ui/stepper";
6 | import { Button } from "@/registry/new-york/ui/button";
7 |
8 | const { Stepper } = defineStepper(
9 | {
10 | id: "step-1",
11 | title: "Step 1",
12 | },
13 | {
14 | id: "step-2",
15 | title: "Step 2",
16 | },
17 | {
18 | id: "step-3",
19 | title: "Step 3",
20 | }
21 | );
22 |
23 | export function StepperDemo() {
24 | return (
25 |
26 | {({ methods }) => (
27 |
28 |
29 | {methods.all.map((step) => (
30 | methods.goTo(step.id)}
34 | >
35 | {step.title}
36 |
37 | ))}
38 |
39 | {methods.switch({
40 | "step-1": (step) => ,
41 | "step-2": (step) => ,
42 | "step-3": (step) => ,
43 | })}
44 |
45 | {!methods.isLast && (
46 |
52 | Previous
53 |
54 | )}
55 |
56 | {methods.isLast ? "Reset" : "Next"}
57 |
58 |
59 |
60 | )}
61 |
62 | );
63 | }
64 |
65 | const Content = ({ id }: { id: string }) => {
66 | return (
67 |
68 | Content for {id}
69 |
70 | );
71 | };
72 |
--------------------------------------------------------------------------------
/apps/docs/content/docs/react/migration/migrating-to-v2.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Migrating to v2
3 | description: Learn how to migrate from v1 to v2
4 | icon: CircleFadingArrowUp
5 | ---
6 |
7 | Stepperize v2 is a complete rewrite of the original Stepperize library. It comes with a few breaking changes and new features.
8 |
9 | ## Breaking Changes
10 |
11 | ### defineSteps helper
12 |
13 | The `defineSteps` function has been removed. Use `defineStepper` instead.
14 |
15 | ```tsx
16 | const Stepper = defineStepper(
17 | { id: "step-1", title: "Label 1", description: "Description 1" },
18 | { id: "step-2", title: "Label 2", description: "Description 2" },
19 | { id: "step-3", title: "Label 3", description: "Description 3" },
20 | { id: "step-4", title: "Label 4", description: "Description 4" }
21 | );
22 | ```
23 |
24 | Also, `defineStepper` only requires one id for each step and the type `Step` no longer
25 | has any other value, since you can send it anything else in favour of automatic inference.
26 |
27 | For more information, see the [Define](/docs/react/api-references/define) section.
28 |
29 | ### Stepper Provider
30 |
31 | The `Stepper` component has been removed. Use `Scoped` from `defineStepper` object instead if you want to use with 1 or more Scopes of steps.
32 |
33 | - We don't need to pass the array of steps to the component anymore.
34 | - The `Stepper` component only accepts one prop, `initialStep`, which is the initial step to be active initially.
35 |
36 | ```tsx
37 | const { Scoped } = defineStepper(
38 | { id: "step-1", title: "Label 1", description: "Description 1" },
39 | { id: "step-2", title: "Label 2", description: "Description 2" },
40 | { id: "step-3", title: "Label 3", description: "Description 3" },
41 | { id: "step-4", title: "Label 4", description: "Description 4" }
42 | );
43 | ```
44 |
45 | For more information, see the [Scoped](/docs/react/api-references/scoped) section.
46 |
47 | ### useStepper hook
48 |
49 | The `useStepper` hook has been removed. Use `useStepper` from `defineStepper` instead.
50 |
51 | ```tsx
52 | const { useStepper } = defineStepper(
53 | { id: "step-1", title: "Label 1", description: "Description 1" },
54 | { id: "step-2", title: "Label 2", description: "Description 2" },
55 | { id: "step-3", title: "Label 3", description: "Description 3" },
56 | { id: "step-4", title: "Label 4", description: "Description 4" }
57 | );
58 | ```
59 |
60 | For more information, see the [Hook](/docs/react/api-references/hook) section.
61 |
--------------------------------------------------------------------------------
/apps/docs/registry/new-york/blocks/stepper-with-icon/components/stepper-with-icon.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { HomeIcon, SettingsIcon, UserIcon } from "lucide-react";
4 | import * as React from "react";
5 |
6 | import { defineStepper } from "@/registry/new-york/blocks/stepper-with-icon/components/ui/stepper";
7 | import { Button } from "@/registry/new-york/ui/button";
8 |
9 | const { Stepper } = defineStepper(
10 | {
11 | id: "step-1",
12 | title: "Step 1",
13 | icon: ,
14 | },
15 | {
16 | id: "step-2",
17 | title: "Step 2",
18 | icon: ,
19 | },
20 | {
21 | id: "step-3",
22 | title: "Step 3",
23 | icon: ,
24 | }
25 | );
26 |
27 | export function StepperWithIcon() {
28 | return (
29 |
30 | {({ methods }) => (
31 |
32 |
33 | {methods.all.map((step) => (
34 | methods.goTo(step.id)}
38 | icon={step.icon}
39 | >
40 | {step.title}
41 |
42 | ))}
43 |
44 | {methods.switch({
45 | "step-1": (step) => ,
46 | "step-2": (step) => ,
47 | "step-3": (step) => ,
48 | })}
49 |
50 | {!methods.isLast && (
51 |
57 | Previous
58 |
59 | )}
60 |
61 | {methods.isLast ? "Reset" : "Next"}
62 |
63 |
64 |
65 | )}
66 |
67 | );
68 | }
69 |
70 | const Content = ({ id }: { id: string }) => {
71 | return (
72 |
73 | Content for {id}
74 |
75 | );
76 | };
77 |
--------------------------------------------------------------------------------
/apps/docs/registry/new-york/blocks/stepper-with-description/components/stepper-with-description.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as React from "react";
4 |
5 | import { defineStepper } from "@/registry/new-york/blocks/stepper-with-description/components/ui/stepper";
6 | import { Button } from "@/registry/new-york/ui/button";
7 |
8 | const { Stepper } = defineStepper(
9 | {
10 | id: "step-1",
11 | title: "Step 1",
12 | description: "This is the first step",
13 | },
14 | {
15 | id: "step-2",
16 | title: "Step 2",
17 | description: "This is the second step",
18 | },
19 | {
20 | id: "step-3",
21 | title: "Step 3",
22 | description: "This is the third step",
23 | }
24 | );
25 |
26 | export function StepperWithDescription() {
27 | return (
28 |
29 | {({ methods }) => (
30 |
31 |
32 | {methods.all.map((step) => (
33 | methods.goTo(step.id)}
37 | >
38 | {step.title}
39 | {step.description}
40 |
41 | ))}
42 |
43 | {methods.switch({
44 | "step-1": (step) => ,
45 | "step-2": (step) => ,
46 | "step-3": (step) => ,
47 | })}
48 |
49 | {!methods.isLast && (
50 |
56 | Previous
57 |
58 | )}
59 |
60 | {methods.isLast ? "Reset" : "Next"}
61 |
62 |
63 |
64 | )}
65 |
66 | );
67 | }
68 |
69 | const Content = ({ id }: { id: string }) => {
70 | return (
71 |
72 | Content for {id}
73 |
74 | );
75 | };
76 |
--------------------------------------------------------------------------------
/apps/docs/content/docs/react/shadcn/examples.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Open in v0
3 | description: Examples for shadcn-stepper with v0
4 | ---
5 |
6 | ## Stepper demo
7 |
8 |
9 |
10 |
11 |
12 | Install the example by copying the following command:
13 |
14 | ```bash
15 | npx shadcn add https://stepperize.vercel.app/r/stepper-demo.json
16 | ```
17 |
18 | ## Stepper with description
19 |
20 |
21 |
22 |
23 |
24 | Install the example by copying the following command:
25 |
26 | ```bash
27 | npx shadcn add https://stepperize.vercel.app/r/stepper-with-description.json
28 | ```
29 |
30 | ## Stepper with react-hook-form
31 |
32 |
33 |
34 |
35 |
36 | Install the example by copying the following command:
37 |
38 | ```bash
39 | npx shadcn add https://stepperize.vercel.app/r/stepper-with-form.json
40 | ```
41 |
42 | ## Stepper with icons
43 |
44 |
45 |
46 |
47 |
48 | Install the example by copying the following command:
49 |
50 | ```bash
51 | npx shadcn add https://stepperize.vercel.app/r/stepper-with-icon.json
52 | ```
53 |
54 | ## Stepper with label orientation
55 |
56 |
57 |
58 |
59 |
60 | Install the example by copying the following command:
61 |
62 | ```bash
63 | npx shadcn add https://stepperize.vercel.app/r/stepper-with-label-orientation.json
64 | ```
65 |
66 | ## Stepper with responsive variant
67 |
68 |
69 |
70 |
71 |
72 | Install the example by copying the following command:
73 |
74 | ```bash
75 | npx shadcn add https://stepperize.vercel.app/r/stepper-with-responsive-variant.json
76 | ```
77 |
78 | ## Stepper with tracking
79 |
80 |
81 |
82 |
83 |
84 | Install the example by copying the following command:
85 |
86 | ```bash
87 | npx shadcn add https://stepperize.vercel.app/r/stepper-with-tracking.json
88 | ```
89 |
90 | ## Stepper with variants
91 |
92 |
93 |
94 |
95 |
96 | Install the example by copying the following command:
97 |
98 | ```bash
99 | npx shadcn add https://stepperize.vercel.app/r/stepper-with-variants.json
100 | ```
101 |
--------------------------------------------------------------------------------
/packages/solid/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | [](https://bundlephobia.com/result?p=@stepperize/solid@latest)
6 | [](https://www.npmjs.com/package/@stepperize/solid)
7 | [](https://www.npmjs.com/package/@stepperize/solid)
8 |
9 | A library for creating step-by-step workflows in your apps
10 |
11 | - 🚀 Fast and efficient
12 | - 🔥 Powerful and flexible
13 | - 📦 Lightweight (<1kB gzipped)
14 | - 🪄 Fully type-safe
15 | - 🔗 Composable architecture
16 | - 🎨 Unstyled for maximum customization
17 |
18 | ## Installation
19 |
20 | ```bash
21 | npm install @stepperize/solid
22 | ```
23 |
24 | ## Quick Start
25 |
26 | 1. Import the constructor:
27 |
28 | ```tsx
29 | import { defineStepper } from "@stepperize/solid";
30 | ```
31 |
32 | 2. Define your steps:
33 |
34 | ```tsx
35 | const { Scoped, useStepper, steps } = defineStepper(
36 | { id: "step-1", title: "Step 1", description: "Description for step 1" },
37 | { id: "step-2", title: "Step 2", description: "Description for step 2" },
38 | { id: "step-3", title: "Step 3", description: "Description for step 3" },
39 | { id: "step-4", title: "Step 4", description: "Description for step 4" }
40 | );
41 | ```
42 |
43 | 3. Use the hook in your components:
44 |
45 | ```tsx
46 | function StepperComponent() {
47 | const { currentStep, nextStep, prevStep } = useStepper();
48 |
49 | return (
50 |
51 |
{currentStep.title}
52 |
{currentStep.description}
53 |
Previous
54 |
Next
55 |
56 | );
57 | }
58 | ```
59 |
60 | ## How It Works
61 |
62 | Stepperize allows you to define a series of steps with unique IDs. When you create your steps using `defineStepper`, you get:
63 |
64 | - A `useStepper` hook for step control
65 | - An array of `steps` for rendering
66 | - An `utils` object with useful functions
67 |
68 | The only required field for each step is the `id`. You can add any additional properties you need, and they'll be fully type-safe when using the hook.
69 |
70 | ## Documentation
71 |
72 | For more detailed information on usage, configuration, and advanced features, visit our [full documentation](https://stepperize.vercel.app).
73 |
74 | ## Contributing
75 |
76 | We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for more details.
77 |
78 | ## License
79 |
80 | Stepperize is [MIT licensed](LICENSE).
81 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | [](https://bundlephobia.com/result?p=@stepperize/react@latest)
6 | [](https://www.npmjs.com/package/@stepperize/react)
7 | [](https://www.npmjs.com/package/@stepperize/react)
8 |
9 | A library for creating step-by-step workflows in your apps
10 |
11 | - 🚀 Fast and efficient
12 | - 🔥 Powerful and flexible
13 | - 📦 Lightweight
14 | - 🪄 Fully type-safe
15 | - 🔗 Composable architecture
16 | - 🎨 Unstyled for maximum customization
17 |
18 | ## Installation
19 |
20 | ```bash
21 | npm install @stepperize/react
22 | ```
23 |
24 | ## Quick Start
25 |
26 | 1. Import the constructor:
27 |
28 | ```tsx
29 | import { defineStepper } from "@stepperize/react";
30 | ```
31 |
32 | 2. Define your steps:
33 |
34 | ```tsx
35 | const { Scoped, useStepper, steps, utils } = defineStepper(
36 | { id: "step-1", title: "Step 1", description: "Description for step 1" },
37 | { id: "step-2", title: "Step 2", description: "Description for step 2" },
38 | { id: "step-3", title: "Step 3", description: "Description for step 3" },
39 | { id: "step-4", title: "Step 4", description: "Description for step 4" }
40 | );
41 | ```
42 |
43 | 3. Use the hook in your components:
44 |
45 | ```tsx
46 | function StepperComponent() {
47 | const { currentStep, nextStep, prevStep } = useStepper();
48 |
49 | return (
50 |
51 |
{currentStep.title}
52 |
{currentStep.description}
53 |
Previous
54 |
Next
55 |
56 | );
57 | }
58 | ```
59 |
60 | ## How It Works
61 |
62 | Stepperize allows you to define a series of steps with unique IDs. When you create your steps using `defineStepper`, you get:
63 |
64 | - A `Scoped` component for context management
65 | - A `useStepper` hook for step control
66 | - An array of `steps` for rendering
67 | - An `utils` object with useful functions
68 |
69 | The only required field for each step is the `id`. You can add any additional properties you need, and they'll be fully type-safe when using the hook.
70 |
71 | ## Documentation
72 |
73 | For more detailed information on usage, configuration, and advanced features, visit our [full documentation](https://stepperize.vercel.app).
74 |
75 | ## Contributing
76 |
77 | We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for more details.
78 |
79 | ## License
80 |
81 | Stepperize is [MIT licensed](LICENSE).
82 |
--------------------------------------------------------------------------------
/packages/vue/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | [](https://bundlephobia.com/result?p=@stepperize/vue@latest)
6 | [](https://www.npmjs.com/package/@stepperize/vue)
7 | [](https://www.npmjs.com/package/@stepperize/vue)
8 |
9 | A library for creating step-by-step workflows in your apps
10 |
11 | - 🚀 Fast and efficient
12 | - 🔥 Powerful and flexible
13 | - 📦 Lightweight (1kB gzipped)
14 | - 🪄 Fully type-safe
15 | - 🔗 Composable architecture
16 | - 🎨 Unstyled for maximum customization
17 |
18 | ## Installation
19 |
20 | ```bash
21 | npm install @stepperize/vue
22 | ```
23 |
24 | ## Quick Start
25 |
26 | 1. Import the constructor:
27 |
28 | ```tsx
29 | import { defineStepper } from "@stepperize/vue";
30 | ```
31 |
32 | 2. Define your steps:
33 |
34 | ```tsx
35 | const { Scoped, useStepper, steps } = defineStepper(
36 | { id: "step-1", title: "Step 1", description: "Description for step 1" },
37 | { id: "step-2", title: "Step 2", description: "Description for step 2" },
38 | { id: "step-3", title: "Step 3", description: "Description for step 3" },
39 | { id: "step-4", title: "Step 4", description: "Description for step 4" }
40 | );
41 | ```
42 |
43 | 3. Use the hook in your components:
44 |
45 | ```tsx
46 | function StepperComponent() {
47 | const { currentStep, nextStep, prevStep } = useStepper();
48 |
49 | return (
50 |
51 |
{currentStep.title}
52 |
{currentStep.description}
53 |
Previous
54 |
Next
55 |
56 | );
57 | }
58 | ```
59 |
60 | ## How It Works
61 |
62 | Stepperize allows you to define a series of steps with unique IDs. When you create your steps using `defineStepper`, you get:
63 |
64 | - A `Scoped` component for context management
65 | - A `useStepper` hook for step control
66 | - An array of `steps` for rendering
67 | - An `utils` object with useful functions
68 |
69 | The only required field for each step is the `id`. You can add any additional properties you need, and they'll be fully type-safe when using the hook.
70 |
71 | ## Documentation
72 |
73 | For more detailed information on usage, configuration, and advanced features, visit our [full documentation](https://stepperize.vercel.app).
74 |
75 | ## Contributing
76 |
77 | We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for more details.
78 |
79 | ## License
80 |
81 | Stepperize is [MIT licensed](LICENSE).
82 |
--------------------------------------------------------------------------------
/packages/react/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | [](https://bundlephobia.com/result?p=@stepperize/react@latest)
6 | [](https://www.npmjs.com/package/@stepperize/react)
7 | [](https://www.npmjs.com/package/@stepperize/react)
8 |
9 | A library for creating step-by-step workflows in your apps
10 |
11 | - 🚀 Fast and efficient
12 | - 🔥 Powerful and flexible
13 | - 📦 Lightweight (1.1kB gzipped)
14 | - 🪄 Fully type-safe
15 | - 🔗 Composable architecture
16 | - 🎨 Unstyled for maximum customization
17 |
18 | ## Installation
19 |
20 | ```bash
21 | npm install @stepperize/react
22 | ```
23 |
24 | ## Quick Start
25 |
26 | 1. Import the constructor:
27 |
28 | ```tsx
29 | import { defineStepper } from "@stepperize/react";
30 | ```
31 |
32 | 2. Define your steps:
33 |
34 | ```tsx
35 | const { Scoped, useStepper, steps } = defineStepper(
36 | { id: "step-1", title: "Step 1", description: "Description for step 1" },
37 | { id: "step-2", title: "Step 2", description: "Description for step 2" },
38 | { id: "step-3", title: "Step 3", description: "Description for step 3" },
39 | { id: "step-4", title: "Step 4", description: "Description for step 4" }
40 | );
41 | ```
42 |
43 | 3. Use the hook in your components:
44 |
45 | ```tsx
46 | function StepperComponent() {
47 | const { currentStep, nextStep, prevStep } = useStepper();
48 |
49 | return (
50 |
51 |
{currentStep.title}
52 |
{currentStep.description}
53 |
Previous
54 |
Next
55 |
56 | );
57 | }
58 | ```
59 |
60 | ## How It Works
61 |
62 | Stepperize allows you to define a series of steps with unique IDs. When you create your steps using `defineStepper`, you get:
63 |
64 | - A `Scoped` component for context management
65 | - A `useStepper` hook for step control
66 | - An array of `steps` for rendering
67 | - An `utils` object with useful functions
68 |
69 | The only required field for each step is the `id`. You can add any additional properties you need, and they'll be fully type-safe when using the hook.
70 |
71 | ## Documentation
72 |
73 | For more detailed information on usage, configuration, and advanced features, visit our [full documentation](https://stepperize.vercel.app).
74 |
75 | ## Contributing
76 |
77 | We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for more details.
78 |
79 | ## License
80 |
81 | Stepperize is [MIT licensed](LICENSE).
82 |
--------------------------------------------------------------------------------
/apps/docs/registry/new-york/blocks/stepper-with-responsive-variant/components/stepper-with-responsive-variant.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as React from "react";
4 |
5 | import { defineStepper } from "@/registry/new-york/blocks/stepper-with-responsive-variant/components/ui/stepper";
6 | import { useMediaQuery } from "@/registry/new-york/blocks/stepper-with-responsive-variant/hooks/use-media-query";
7 | import { Button } from "@/registry/new-york/ui/button";
8 |
9 | const { Stepper } = defineStepper(
10 | {
11 | id: "step-1",
12 | title: "Step 1",
13 | },
14 | {
15 | id: "step-2",
16 | title: "Step 2",
17 | },
18 | {
19 | id: "step-3",
20 | title: "Step 3",
21 | }
22 | );
23 |
24 | export function StepperWithResponsiveVariant() {
25 | const isMobile = useMediaQuery("(max-width: 768px)");
26 | return (
27 |
31 | {({ methods }) => (
32 |
33 |
34 | {methods.all.map((step) => (
35 | methods.goTo(step.id)}
39 | >
40 | {step.title}
41 | {isMobile &&
42 | methods.when(step.id, (step) => (
43 |
44 |
45 | Content for {step.id}
46 |
47 |
48 | ))}
49 |
50 | ))}
51 |
52 | {!isMobile &&
53 | methods.switch({
54 | "step-1": (step) => ,
55 | "step-2": (step) => ,
56 | "step-3": (step) => ,
57 | })}
58 |
59 | {!methods.isLast && (
60 |
66 | Previous
67 |
68 | )}
69 |
70 | {methods.isLast ? "Reset" : "Next"}
71 |
72 |
73 |
74 | )}
75 |
76 | );
77 | }
78 |
79 | const Content = ({ id }: { id: string }) => {
80 | return (
81 |
82 | Content for {id}
83 |
84 | );
85 | };
86 |
--------------------------------------------------------------------------------
/apps/docs/app/og/[...slug]/og.tsx:
--------------------------------------------------------------------------------
1 | import type { ImageResponseOptions } from "next/dist/compiled/@vercel/og/types";
2 | import { ImageResponse } from "next/og";
3 | import type { ReactElement, ReactNode } from "react";
4 |
5 | interface GenerateProps {
6 | title: ReactNode;
7 | description?: ReactNode;
8 | primaryTextColor?: string;
9 | }
10 |
11 | export function generateOGImage(
12 | options: GenerateProps & ImageResponseOptions
13 | ): ImageResponse {
14 | const { title, description, primaryTextColor, ...rest } = options;
15 |
16 | return new ImageResponse(
17 | generate({
18 | title,
19 | description,
20 | primaryTextColor,
21 | }),
22 | {
23 | width: 1200,
24 | height: 630,
25 | ...rest,
26 | }
27 | );
28 | }
29 |
30 | export function generate({
31 | primaryTextColor = "rgb(255,150,255)",
32 | ...props
33 | }: GenerateProps): ReactElement {
34 | return (
35 |
45 |
54 |
60 | {props.title}
61 |
62 |
68 | {props.description}
69 |
70 |
79 |
91 | Stepperize
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
106 | Stepperize
107 |
108 |
109 |
110 |
111 | );
112 | }
113 |
--------------------------------------------------------------------------------
/apps/docs/content/docs/react/api-references/scoped.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Scoped
3 | description: Using the Scoped component to share stepper state across child components
4 | ---
5 |
6 | The `Scoped` component acts as a provider, allowing you to synchronize stepper state via `useStepper` across child components.
7 |
8 | ## Basic Usage
9 |
10 | ```tsx twoslash
11 | import * as React from "react";
12 | import { defineStepper } from "@stepperize/react";
13 |
14 | const Stepper = defineStepper(
15 | { id: "first", title: "First" },
16 | { id: "second", title: "Second" },
17 | { id: "last", title: "Last" }
18 | );
19 |
20 | export const MyScopedStepper = () => (
21 |
22 |
23 |
24 |
25 | );
26 |
27 | const StepContent = () => {
28 | const { when } = Stepper.useStepper();
29 | return (
30 |
31 | {when("first", (step) => (
32 | Starting with {step.title}
33 | ))}
34 | {when("second", (step) => (
35 | In the middle: {step.title}
36 | ))}
37 |
38 | );
39 | };
40 |
41 | const StepNavigation = () => {
42 | const { isLast, reset, next, when } = Stepper.useStepper();
43 | return (
44 |
45 | {when(
46 | "last",
47 | () => "Reset",
48 | () => "Next"
49 | )}
50 |
51 | );
52 | };
53 | ```
54 |
55 |
56 | Use `Scoped` only when child components need access to stepper state.
57 | Otherwise, use the hook directly in your component.
58 |
59 |
60 | ## API Reference
61 |
62 | | Name | Type | Description | Required |
63 | | --------------- | -------------------------- | ------------------------------------- | -------- |
64 | | initialStep | `string` | ID of the initial step to display. | No |
65 | | initialMetadata | `Record` | Initial metadata for the stepper. | No |
66 | | children | `React.ReactNode` | Content to render inside the stepper. | Yes |
67 |
68 | ## Nested Scopes
69 |
70 | You can nest multiple scopes for more complex stepper hierarchies:
71 |
72 | ```tsx
73 | import * as React from "react";
74 | import { defineStepper } from "@stepperize/react";
75 | // ---cut-before---
76 | const GlobalStepper = defineStepper(
77 | { id: "start", title: "Start" },
78 | { id: "middle", title: "Middle" },
79 | { id: "end", title: "End" }
80 | );
81 |
82 | const LocalStepper = defineStepper(
83 | { id: "sub1", title: "Sub-step 1" },
84 | { id: "sub2", title: "Sub-step 2" }
85 | );
86 |
87 | export const NestedSteppers = () => (
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 | );
97 |
98 | // Use GlobalStepper.useStepper() for global scope
99 | // Use LocalStepper.useStepper() for local scope
100 | ```
101 |
102 | This structure allows for independent control of global and local stepper states.
103 |
--------------------------------------------------------------------------------
/apps/docs/components/home/cta.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { cn } from "@/lib/cn";
4 | import { ExternalLink } from "lucide-react";
5 | import { Github } from "lucide-react";
6 | import { motion } from "motion/react";
7 |
8 | export const Cta = ({ className }: { className?: string }) => {
9 | return (
10 |
17 |
22 |
23 |
24 |
31 | Ready to Simplify Your Multi-Step Flows?
32 |
33 |
34 |
41 | Start building intuitive step-by-step experiences with Stepperize
42 | today
43 |
44 |
45 |
52 |
58 |
59 | View on GitHub
60 |
61 |
69 |
70 | NPM Package
71 |
72 |
73 |
74 |
75 | );
76 | };
77 |
--------------------------------------------------------------------------------
/apps/docs/content/docs/react/api-references/schema-validation.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Schema Validation
3 | description: Learn how to validate your stepper with any schema library
4 | ---
5 |
6 | Stepperize provides a way to add schemas to your steps with any schema library. This is very useful for working with forms and libraries such as React Hook Form, Conform, etc.
7 |
8 | Let's see how we can create our validation schemas and have them within our stepper.
9 |
10 | ## Zod
11 |
12 | ```tsx twoslash
13 | import { z } from "zod";
14 | import { defineStepper } from "@stepperize/react";
15 |
16 | const Schema = z.object({
17 | email: z.string().email(),
18 | password: z.string().min(8),
19 | });
20 |
21 | type MyFormValues = z.infer;
22 |
23 | const stepperInstance = defineStepper({
24 | id: "personal",
25 | label: "Personal Information",
26 | schema: Schema,
27 | });
28 | ```
29 |
30 | ## ValiBot
31 |
32 | ```tsx twoslash
33 | import * as v from "valibot";
34 | import { defineStepper } from "@stepperize/react";
35 |
36 | const Schema = v.object({
37 | email: v.pipe(v.string(), v.email()),
38 | password: v.pipe(v.string(), v.minLength(8)),
39 | });
40 |
41 | type MyFormValues = v.InferInput;
42 |
43 | const stepperInstance = defineStepper({
44 | id: "personal",
45 | label: "Personal Information",
46 | schema: Schema,
47 | });
48 | ```
49 |
50 | ## ArkType
51 |
52 | ```tsx twoslash
53 | import { type } from "arktype";
54 | import { defineStepper } from "@stepperize/react";
55 |
56 | const Schema = type({
57 | email: "string.email",
58 | password: "string >= 8",
59 | });
60 |
61 | type MyFormValues = typeof Schema.infer;
62 |
63 | const stepperInstance = defineStepper({
64 | id: "personal",
65 | label: "Personal Information",
66 | schema: Schema,
67 | });
68 | ```
69 |
70 | ## Working with multiple schemas and steps
71 |
72 | Let's say we have a series of steps and we will use them with forms, each of which has a validation schema. All you have to do is add the schemas with the key of your choice, and that's it.
73 |
74 | Let's see how it works in a practical example with Zod.
75 |
76 | ```tsx twoslash
77 | import { z } from "zod";
78 | import { defineStepper } from "@stepperize/react";
79 |
80 | const FirstStepSchema = z.object({
81 | name: z.string().min(1),
82 | lastName: z.string().min(1),
83 | });
84 |
85 | const SecondStepSchema = z.object({
86 | email: z.string().email(),
87 | password: z.string().min(8),
88 | });
89 |
90 | const ThirdStepSchema = z.object({});
91 |
92 | const stepperInstance = defineStepper(
93 | {
94 | id: "personal-info",
95 | label: "Personal Information",
96 | schema: FirstStepSchema,
97 | },
98 | {
99 | id: "account",
100 | label: "Account Information",
101 | schema: SecondStepSchema,
102 | },
103 | {
104 | id: "complete",
105 | label: "Complete",
106 | schema: ThirdStepSchema,
107 | }
108 | );
109 | ```
110 |
111 |
112 | You've probably noticed that the last step has an empty schema. This is
113 | because stepperize infers all the steps, and when you want to send your
114 | `currentStep.schema`, one of them could be undefined. To avoid this, you can
115 | simply add an empty schema. Or, if you prefer, you can choose not to do this
116 | and validate for yourself whether the schema exists.
117 |
118 |
--------------------------------------------------------------------------------
/apps/docs/components/layout/root-toggle.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 | import { ChevronsUpDown } from 'lucide-react';
3 | import { type HTMLAttributes, type ReactNode, useMemo, useState } from 'react';
4 | import Link from 'next/link';
5 | import { usePathname } from 'next/navigation';
6 | import { cn } from '../../lib/cn';
7 | import { isActive } from '../../lib/is-active';
8 | import { useSidebar } from 'fumadocs-ui/provider';
9 | import { Popover, PopoverContent, PopoverTrigger } from '../ui/popover';
10 |
11 | export interface Option {
12 | /**
13 | * Redirect URL of the folder, usually the index page
14 | */
15 | url: string;
16 |
17 | icon?: ReactNode;
18 | title: ReactNode;
19 | description?: ReactNode;
20 |
21 | /**
22 | * Detect from a list of urls
23 | */
24 | urls?: Set;
25 |
26 | props?: HTMLAttributes;
27 | }
28 |
29 | export function RootToggle({
30 | options,
31 | placeholder,
32 | ...props
33 | }: {
34 | placeholder?: ReactNode;
35 | options: Option[];
36 | } & HTMLAttributes) {
37 | const [open, setOpen] = useState(false);
38 | const { closeOnRedirect } = useSidebar();
39 | const pathname = usePathname();
40 |
41 | const selected = useMemo(() => {
42 | return options.findLast((item) =>
43 | item.urls
44 | ? item.urls.has(
45 | pathname.endsWith('/') ? pathname.slice(0, -1) : pathname,
46 | )
47 | : isActive(item.url, pathname, true),
48 | );
49 | }, [options, pathname]);
50 |
51 | const onClick = () => {
52 | closeOnRedirect.current = false;
53 | setOpen(false);
54 | };
55 |
56 | const item = selected ? : placeholder;
57 |
58 | return (
59 |
60 | {item ? (
61 |
68 | {item}
69 |
70 |
71 | ) : null}
72 |
73 | {options.map((item) => (
74 |
87 |
88 |
89 | ))}
90 |
91 |
92 | );
93 | }
94 |
95 | function Item(props: Option) {
96 | return (
97 | <>
98 | {props.icon}
99 |
100 |
{props.title}
101 | {props.description ? (
102 |
103 | {props.description}
104 |
105 | ) : null}
106 |
107 | >
108 | );
109 | }
110 |
--------------------------------------------------------------------------------
/apps/docs/registry/new-york/blocks/stepper-with-label-orientation/components/stepper-with-label-orientation.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as React from "react";
4 |
5 | import { defineStepper } from "@/registry/new-york/blocks/stepper-with-label-orientation/components/ui/stepper";
6 | import { Button } from "@/registry/new-york/ui/button";
7 | import { Label } from "@/registry/new-york/ui/label";
8 | import { RadioGroup, RadioGroupItem } from "@/registry/new-york/ui/radio-group";
9 |
10 | type LabelOrientation = "horizontal" | "vertical";
11 |
12 | const { Stepper } = defineStepper(
13 | {
14 | id: "step-1",
15 | title: "Step 1",
16 | },
17 | {
18 | id: "step-2",
19 | title: "Step 2",
20 | },
21 | {
22 | id: "step-3",
23 | title: "Step 3",
24 | }
25 | );
26 |
27 | export function StepperWithLabelOrientation() {
28 | const [labelOrientation, setLabelOrientation] =
29 | React.useState("horizontal");
30 | return (
31 |
32 |
35 | setLabelOrientation(value as LabelOrientation)
36 | }
37 | >
38 |
39 |
40 | Horizontal
41 |
42 |
43 |
44 | Vertical
45 |
46 |
47 |
52 | {({ methods }) => (
53 |
54 |
55 | {methods.all.map((step) => (
56 | methods.goTo(step.id)}
60 | >
61 | {step.title}
62 |
63 | ))}
64 |
65 | {methods.switch({
66 | "step-1": (step) => ,
67 | "step-2": (step) => ,
68 | "step-3": (step) => ,
69 | })}
70 |
71 | {!methods.isLast && (
72 |
78 | Previous
79 |
80 | )}
81 |
82 | {methods.isLast ? "Reset" : "Next"}
83 |
84 |
85 |
86 | )}
87 |
88 |
89 | );
90 | }
91 |
92 | const Content = ({ id }: { id: string }) => {
93 | return (
94 |
95 | Content for {id}
96 |
97 | );
98 | };
99 |
--------------------------------------------------------------------------------
/apps/docs/registry/new-york/blocks/stepper-with-tracking/components/stepper-with-tracking.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as React from "react";
4 |
5 | import { defineStepper } from "@/registry/new-york/blocks/stepper-with-tracking/components/ui/stepper";
6 | import { Button } from "@/registry/new-york/ui/button";
7 | import { Label } from "@/registry/new-york/ui/label";
8 | import { RadioGroup, RadioGroupItem } from "@/registry/new-york/ui/radio-group";
9 |
10 | const { Stepper } = defineStepper(
11 | {
12 | id: "step-1",
13 | title: "Step 1",
14 | },
15 | {
16 | id: "step-2",
17 | title: "Step 2",
18 | },
19 | {
20 | id: "step-3",
21 | title: "Step 3",
22 | },
23 | {
24 | id: "step-4",
25 | title: "Step 4",
26 | },
27 | {
28 | id: "step-5",
29 | title: "Step 5",
30 | },
31 | {
32 | id: "step-6",
33 | title: "Step 6",
34 | }
35 | );
36 |
37 | export function StepperWithTracking() {
38 | const [tracking, setTracking] = React.useState(false);
39 | return (
40 |
41 |
setTracking(value === "true")}
44 | >
45 |
46 |
47 | Tracking
48 |
49 |
50 |
51 | No Tracking
52 |
53 |
54 |
59 | {({ methods }) => (
60 |
61 |
62 | {methods.all.map((step) => (
63 | methods.goTo(step.id)}
67 | >
68 | {step.title}
69 | {methods.when(step.id, () => (
70 |
71 |
72 |
73 | Content for {step.id}
74 |
75 |
76 |
77 | {!methods.isLast && (
78 |
84 | Previous
85 |
86 | )}
87 |
92 | {methods.isLast ? "Reset" : "Next"}
93 |
94 |
95 |
96 | ))}
97 |
98 | ))}
99 |
100 |
101 | )}
102 |
103 |
104 | );
105 | }
106 |
--------------------------------------------------------------------------------
/packages/solid/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @stepperize/solid
2 |
3 | ## 1.0.7
4 |
5 | ### Patch Changes
6 |
7 | - Updated dependencies [[`9c01f9e`](https://github.com/damianricobelli/stepperize/commit/9c01f9ebe38fea2ea69bf7216977327503945a81)]:
8 | - @stepperize/core@1.2.7
9 |
10 | ## 1.0.6
11 |
12 | ### Patch Changes
13 |
14 | - [#140](https://github.com/damianricobelli/stepperize/pull/140) [`df6fe81`](https://github.com/damianricobelli/stepperize/commit/df6fe81274ce618cb28469c23db73a8f376b542a) Thanks [@damianricobelli](https://github.com/damianricobelli)! - chore: update package versions and fix icon component props
15 |
16 | ## 1.0.5
17 |
18 | ### Patch Changes
19 |
20 | - [#131](https://github.com/damianricobelli/stepperize/pull/131) [`095f052`](https://github.com/damianricobelli/stepperize/commit/095f0525140b239f149478c548ea3f534ec014f2) Thanks [@damianricobelli](https://github.com/damianricobelli)! - core: fix match issue
21 |
22 | - Updated dependencies [[`095f052`](https://github.com/damianricobelli/stepperize/commit/095f0525140b239f149478c548ea3f534ec014f2)]:
23 | - @stepperize/core@1.2.6
24 |
25 | ## 1.0.4
26 |
27 | ### Patch Changes
28 |
29 | - [`5fdf934`](https://github.com/damianricobelli/stepperize/commit/5fdf9344f38a8673220adbc57f8ea55489883563) Thanks [@damianricobelli](https://github.com/damianricobelli)! - Add main entry point
30 |
31 | - Updated dependencies [[`5fdf934`](https://github.com/damianricobelli/stepperize/commit/5fdf9344f38a8673220adbc57f8ea55489883563)]:
32 | - @stepperize/core@1.2.5
33 |
34 | ## 1.0.3
35 |
36 | ### Patch Changes
37 |
38 | - [#119](https://github.com/damianricobelli/stepperize/pull/119) [`921bb62`](https://github.com/damianricobelli/stepperize/commit/921bb6297a0f370fbe5cbf4689b5a698207ee62c) Thanks [@longzheng](https://github.com/longzheng)! - Update package.json `types` to match filename
39 |
40 | - Updated dependencies [[`921bb62`](https://github.com/damianricobelli/stepperize/commit/921bb6297a0f370fbe5cbf4689b5a698207ee62c)]:
41 | - @stepperize/core@1.2.4
42 |
43 | ## 1.0.2
44 |
45 | ### Patch Changes
46 |
47 | - [`c636688`](https://github.com/damianricobelli/stepperize/commit/c6366889d593b0b89b794309f5a530df7596089d) Thanks [@damianricobelli](https://github.com/damianricobelli)! - chore: add defineStepper jsdoc
48 |
49 | ## 1.0.1
50 |
51 | ### Patch Changes
52 |
53 | - [#115](https://github.com/damianricobelli/stepperize/pull/115) [`a51614f`](https://github.com/damianricobelli/stepperize/commit/a51614f802b5d0f9e1a0d4936166d3d56b01692b) Thanks [@damianricobelli](https://github.com/damianricobelli)! - fix: peer deps
54 |
55 | - Updated dependencies [[`a51614f`](https://github.com/damianricobelli/stepperize/commit/a51614f802b5d0f9e1a0d4936166d3d56b01692b)]:
56 | - @stepperize/core@1.2.3
57 |
58 | ## 1.0.0
59 |
60 | ### Major Changes
61 |
62 | - [#113](https://github.com/damianricobelli/stepperize/pull/113) [`788ee09`](https://github.com/damianricobelli/stepperize/commit/788ee0956b3dda7965f37e1483b29b2c7a9b8fc4) Thanks [@damianricobelli](https://github.com/damianricobelli)! - feat: add solid js support, improve package configurations and fix match method
63 |
64 | ### Patch Changes
65 |
66 | - Updated dependencies [[`788ee09`](https://github.com/damianricobelli/stepperize/commit/788ee0956b3dda7965f37e1483b29b2c7a9b8fc4)]:
67 | - @stepperize/core@1.2.2
68 |
69 | ## 0.1.2
70 |
71 | ### Patch Changes
72 |
73 | - [#85](https://github.com/damianricobelli/stepperize/pull/85) [`752f9a6`](https://github.com/damianricobelli/stepperize/commit/752f9a6907cc5e7e623a66350c82eeba9559fea7) Thanks [@damianricobelli](https://github.com/damianricobelli)! - chore: lint and fix ci
74 |
75 | ## 0.1.1
76 |
77 | ### Patch Changes
78 |
79 | - [#80](https://github.com/damianricobelli/stepperize/pull/80) [`e0f5de7`](https://github.com/damianricobelli/stepperize/commit/e0f5de733f9f42527e62cdb35f8e6ca42063b187) Thanks [@damianricobelli](https://github.com/damianricobelli)! - chore: update all packages deps and upgrade docs to latest fumadocs version
80 |
--------------------------------------------------------------------------------
/apps/docs/components/accordion.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import * as AccordionPrimitive from '@radix-ui/react-accordion';
4 | import type {
5 | AccordionMultipleProps,
6 | AccordionSingleProps,
7 | } from '@radix-ui/react-accordion';
8 | import { Check, ChevronRight, LinkIcon } from 'lucide-react';
9 | import {
10 | forwardRef,
11 | type ComponentPropsWithoutRef,
12 | useState,
13 | useEffect,
14 | } from 'react';
15 | import { cn } from '../lib/cn';
16 | import { useCopyButton } from '../lib/use-copy-button';
17 | import { buttonVariants } from './ui/button';
18 |
19 | export const Accordions = forwardRef<
20 | HTMLDivElement,
21 | | Omit
22 | | Omit
23 | >(({ type = 'single', className, defaultValue, ...props }, ref) => {
24 | const [value, setValue] = useState(
25 | type === 'single' ? (defaultValue ?? '') : (defaultValue ?? []),
26 | );
27 |
28 | useEffect(() => {
29 | const id = window.location.hash.substring(1);
30 |
31 | if (id.length > 0)
32 | setValue((prev) => (typeof prev === 'string' ? id : [id, ...prev]));
33 | }, []);
34 |
35 | return (
36 | // @ts-expect-error -- Multiple types
37 |
49 | );
50 | });
51 |
52 | Accordions.displayName = 'Accordions';
53 |
54 | export const Accordion = forwardRef<
55 | HTMLDivElement,
56 | Omit, 'value'> & {
57 | title: string;
58 | }
59 | >(({ title, className, id, children, ...props }, ref) => {
60 | return (
61 |
68 |
72 |
73 |
74 | {title}
75 |
76 | {id ? : null}
77 |
78 |
79 | {children}
80 |
81 |
82 | );
83 | });
84 |
85 | function CopyButton({ id }: { id: string }): React.ReactElement {
86 | const [checked, onClick] = useCopyButton(() => {
87 | const url = new URL(window.location.href);
88 | url.hash = id;
89 |
90 | void navigator.clipboard.writeText(url.toString());
91 | });
92 |
93 | return (
94 |
105 | {checked ? (
106 |
107 | ) : (
108 |
109 | )}
110 |
111 | );
112 | }
113 |
114 | Accordion.displayName = 'Accordion';
115 |
--------------------------------------------------------------------------------
/packages/core/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @stepperize/core
2 |
3 | ## 1.2.7
4 |
5 | ### Patch Changes
6 |
7 | - [#147](https://github.com/damianricobelli/stepperize/pull/147) [`9c01f9e`](https://github.com/damianricobelli/stepperize/commit/9c01f9ebe38fea2ea69bf7216977327503945a81) Thanks [@damianricobelli](https://github.com/damianricobelli)! - chore: add tests for core and react packages
8 |
9 | ## 1.2.6
10 |
11 | ### Patch Changes
12 |
13 | - [#131](https://github.com/damianricobelli/stepperize/pull/131) [`095f052`](https://github.com/damianricobelli/stepperize/commit/095f0525140b239f149478c548ea3f534ec014f2) Thanks [@damianricobelli](https://github.com/damianricobelli)! - core: fix match issue
14 |
15 | ## 1.2.5
16 |
17 | ### Patch Changes
18 |
19 | - [`5fdf934`](https://github.com/damianricobelli/stepperize/commit/5fdf9344f38a8673220adbc57f8ea55489883563) Thanks [@damianricobelli](https://github.com/damianricobelli)! - Add main entry point
20 |
21 | ## 1.2.4
22 |
23 | ### Patch Changes
24 |
25 | - [#119](https://github.com/damianricobelli/stepperize/pull/119) [`921bb62`](https://github.com/damianricobelli/stepperize/commit/921bb6297a0f370fbe5cbf4689b5a698207ee62c) Thanks [@longzheng](https://github.com/longzheng)! - Update package.json `types` to match filename
26 |
27 | ## 1.2.3
28 |
29 | ### Patch Changes
30 |
31 | - [#115](https://github.com/damianricobelli/stepperize/pull/115) [`a51614f`](https://github.com/damianricobelli/stepperize/commit/a51614f802b5d0f9e1a0d4936166d3d56b01692b) Thanks [@damianricobelli](https://github.com/damianricobelli)! - fix: peer deps
32 |
33 | ## 1.2.2
34 |
35 | ### Patch Changes
36 |
37 | - [#113](https://github.com/damianricobelli/stepperize/pull/113) [`788ee09`](https://github.com/damianricobelli/stepperize/commit/788ee0956b3dda7965f37e1483b29b2c7a9b8fc4) Thanks [@damianricobelli](https://github.com/damianricobelli)! - feat: add solid js support, improve package configurations and fix match method
38 |
39 | ## 1.2.1
40 |
41 | ### Patch Changes
42 |
43 | - [#111](https://github.com/damianricobelli/stepperize/pull/111) [`5acc49b`](https://github.com/damianricobelli/stepperize/commit/5acc49b4387d38d3cf0d8c7d192f36ae44860f66) Thanks [@damianricobelli](https://github.com/damianricobelli)! - Update docs
44 |
45 | ## 1.2.0
46 |
47 | ### Minor Changes
48 |
49 | - [#107](https://github.com/damianricobelli/stepperize/pull/107) [`8360286`](https://github.com/damianricobelli/stepperize/commit/83602861e8b21ef7c9943356ed93e37044e5b145) Thanks [@damianricobelli](https://github.com/damianricobelli)! - Add before and after go to feature
50 |
51 | ## 1.1.1
52 |
53 | ### Patch Changes
54 |
55 | - [#101](https://github.com/damianricobelli/stepperize/pull/101) [`ac936a5`](https://github.com/damianricobelli/stepperize/commit/ac936a5eecfc3eed959ce83ab45045868ed2e197) Thanks [@damianricobelli](https://github.com/damianricobelli)! - stepperize/react v5
56 |
57 | ## 1.1.0
58 |
59 | ### Minor Changes
60 |
61 | - [#98](https://github.com/damianricobelli/stepperize/pull/98) [`086e074`](https://github.com/damianricobelli/stepperize/commit/086e074ad39c731229910daa26e6ed099ddb923a) Thanks [@damianricobelli](https://github.com/damianricobelli)! - feat: new before/next functions
62 |
63 | ## 1.0.0
64 |
65 | ### Major Changes
66 |
67 | - [#88](https://github.com/damianricobelli/stepperize/pull/88) [`46591cc`](https://github.com/damianricobelli/stepperize/commit/46591cc7aabf6d2730cf8296166792a4c33c2d2b) Thanks [@damianricobelli](https://github.com/damianricobelli)! - chore: update package json
68 |
69 | ## 1.0.0
70 |
71 | ### Major Changes
72 |
73 | - [#29](https://github.com/damianricobelli/stepperize/pull/29) [`f1ce841`](https://github.com/damianricobelli/stepperize/commit/f1ce841411844be787339e269de1a9003ebe715b) Thanks [@alexzhang1030](https://github.com/alexzhang1030)! - refactor: extract common logic to @stepprize/core
74 |
75 | ### Patch Changes
76 |
77 | - [#85](https://github.com/damianricobelli/stepperize/pull/85) [`752f9a6`](https://github.com/damianricobelli/stepperize/commit/752f9a6907cc5e7e623a66350c82eeba9559fea7) Thanks [@damianricobelli](https://github.com/damianricobelli)! - chore: lint and fix ci
78 |
--------------------------------------------------------------------------------
/packages/react/src/define-stepper.ts:
--------------------------------------------------------------------------------
1 | import type { Get, Metadata, Step, Stepper } from "@stepperize/core";
2 | import {
3 | executeTransition,
4 | generateCommonStepperUseFns,
5 | generateStepperUtils,
6 | getInitialMetadata,
7 | getInitialStepIndex,
8 | updateStepIndex,
9 | } from "@stepperize/core";
10 | import * as React from "react";
11 | import type { StepperReturn } from "./types";
12 |
13 | /**
14 | * Creates a stepper context and utility functions for managing stepper state.
15 | *
16 | * @param steps - The steps to be included in the stepper.
17 | * @returns An object containing the stepper context and utility functions.
18 | */
19 | export const defineStepper = (...steps: Steps): StepperReturn => {
20 | const Context = React.createContext | null>(null);
21 |
22 | const utils = generateStepperUtils(...steps);
23 |
24 | const useStepper = (config?: {
25 | initialStep?: Get.Id;
26 | initialMetadata?: Partial, Metadata>>;
27 | }) => {
28 | const { initialStep, initialMetadata } = config ?? {};
29 | const initialStepIndex = React.useMemo(() => getInitialStepIndex(steps, initialStep), [initialStep]);
30 |
31 | const [stepIndex, setStepIndex] = React.useState(initialStepIndex);
32 | const [metadata, setMetadata] = React.useState(() => getInitialMetadata(steps, initialMetadata));
33 |
34 | const stepper = React.useMemo(() => {
35 | const current = steps[stepIndex];
36 | const isLast = stepIndex === steps.length - 1;
37 | const isFirst = stepIndex === 0;
38 |
39 | return {
40 | all: steps,
41 | current,
42 | isLast,
43 | isFirst,
44 | metadata,
45 | setMetadata(id, data) {
46 | setMetadata((prev) => {
47 | if (prev[id] === data) return prev;
48 | return { ...prev, [id]: data };
49 | });
50 | },
51 | getMetadata(id) {
52 | return metadata[id];
53 | },
54 | resetMetadata(keepInitialMetadata) {
55 | setMetadata(getInitialMetadata(steps, keepInitialMetadata ? initialMetadata : undefined));
56 | },
57 | next() {
58 | updateStepIndex(steps, stepIndex + 1, (newIndex) => {
59 | setStepIndex(newIndex);
60 | });
61 | },
62 | prev() {
63 | updateStepIndex(steps, stepIndex - 1, (newIndex) => {
64 | setStepIndex(newIndex);
65 | });
66 | },
67 | get(id) {
68 | return steps.find((step) => step.id === id);
69 | },
70 | goTo(id) {
71 | const index = steps.findIndex((s) => s.id === id);
72 | if (index === -1) throw new Error(`Step with id "${id}" not found.`);
73 | updateStepIndex(steps, index, (newIndex) => {
74 | setStepIndex(newIndex);
75 | });
76 | },
77 | reset() {
78 | updateStepIndex(steps, getInitialStepIndex(steps, initialStep), (newIndex) => {
79 | setStepIndex(newIndex);
80 | });
81 | },
82 | async beforeNext(callback) {
83 | await executeTransition({ stepper, direction: "next", callback, before: true });
84 | },
85 | async afterNext(callback) {
86 | this.next();
87 | await executeTransition({ stepper, direction: "next", callback, before: false });
88 | },
89 | async beforePrev(callback) {
90 | await executeTransition({ stepper, direction: "prev", callback, before: true });
91 | },
92 | async afterPrev(callback) {
93 | this.prev();
94 | await executeTransition({ stepper, direction: "prev", callback, before: false });
95 | },
96 | async beforeGoTo(id, callback) {
97 | await executeTransition({ stepper, direction: "goTo", callback, before: true, targetId: id });
98 | },
99 | async afterGoTo(id, callback) {
100 | this.goTo(id);
101 | await executeTransition({ stepper, direction: "goTo", callback, before: false, targetId: id });
102 | },
103 | ...generateCommonStepperUseFns(steps, current, stepIndex),
104 | } as Stepper;
105 | }, [stepIndex, metadata]);
106 |
107 | return stepper;
108 | };
109 |
110 | return {
111 | steps,
112 | utils,
113 | Scoped: ({ initialStep, initialMetadata, children }) =>
114 | React.createElement(
115 | Context.Provider,
116 | {
117 | value: useStepper({ initialStep, initialMetadata }),
118 | },
119 | children,
120 | ),
121 | useStepper: (props = {}) => React.useContext(Context) ?? useStepper(props),
122 | };
123 | };
124 |
--------------------------------------------------------------------------------
/apps/docs/content/docs/solid/api-references/utils.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Utils
3 | description: Pure utility functions for working with steps
4 | ---
5 |
6 | The `Utils` object provides a set of pure functions for working with steps. These functions help you query and traverse step collections without modifying state or requiring stepper context.
7 |
8 | ## Functions
9 |
10 | | Function | Description | Parameters | Returns |
11 | | -------------- | -------------------------------------------------------------------------- | -------------------------------------------------- | -------------------------------------------------------------------------------- |
12 | | `getAll` | Retrieves all steps in the stepper. | None | An array of all steps |
13 | | `get` | Retrieves a step by its ID. | `id`: The ID of the step to retrieve | The step with the specified ID |
14 | | `getIndex` | Retrieves the index of a step by its ID. | `id`: The ID of the step to retrieve the index for | The index of the step |
15 | | `getByIndex` | Retrieves a step by its index. | `index`: The index of the step to retrieve | The step at the specified index |
16 | | `getFirst` | Retrieves the first step in the stepper. | None | The first step |
17 | | `getLast` | Retrieves the last step in the stepper. | None | The last step |
18 | | `getNext` | Retrieves the next step after the specified ID. | `id`: The ID of the current step | The next step |
19 | | `getPrev` | Retrieves the previous step before the specified ID. | `id`: The ID of the current step | The previous step |
20 | | `getNeighbors` | Retrieves the neighboring steps (previous and next) of the specified step. | `id`: The ID of the current step | An object containing the previous and next steps, or `null` if they do not exist |
21 |
22 | ## Usage Example
23 |
24 | Here's a quick example of how you might use the `Utils` object in a Solid component:
25 |
26 | ```tsx twoslash
27 | import { defineStepper } from "@stepperize/solid";
28 |
29 | // Define the stepper with the steps
30 | const { utils } = defineStepper(
31 | { id: "step1", name: "Step 1" },
32 | { id: "step2", name: "Step 2" },
33 | { id: "step3", name: "Step 3" },
34 | { id: "step4", name: "Step 4" }
35 | );
36 |
37 | export function StepNavigator() {
38 | // Fetch all steps
39 | const allSteps = utils.getAll(); // Result: Array of all steps
40 |
41 | // Fetch the first and last steps
42 | const firstStep = utils.getFirst(); // Result: { id: 'step1', name: 'Step 1' }
43 | const lastStep = utils.getLast(); // Result: { id: 'step4', name: 'Step 4' }
44 |
45 | // Get a step by ID
46 | const stepById = utils.get("step3"); // Result: { id: 'step3', name: 'Step 3' }
47 |
48 | // Get the index of a step by ID
49 | const indexOfStep2 = utils.getIndex("step2"); // Result: 1
50 |
51 | // Get a step by its index
52 | const stepAtIndex2 = utils.getByIndex(2); // Result: { id: 'step3', name: 'Step 3' }
53 |
54 | // Get the next and previous steps
55 | const nextStepAfter1 = utils.getNext("step1"); // Result: { id: 'step2', name: 'Step 2' }
56 | const prevStepBefore3 = utils.getPrev("step3"); // Result: { id: 'step2', name: 'Step 2' }
57 |
58 | // Get neighbors of a step
59 | const neighborsOfStep2 = utils.getNeighbors("step2");
60 | // Result: { prev: { id: 'step1', name: 'Step 1' }, next: { id: 'step3', name: 'Step 3' } }
61 | }
62 | ```
63 |
--------------------------------------------------------------------------------
/apps/docs/content/docs/react/api-references/utils.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Utils
3 | description: Pure utility functions for working with steps
4 | ---
5 |
6 | The `Utils` object provides a set of pure functions for working with steps. These functions help you query and traverse step collections without modifying state or requiring stepper context.
7 |
8 | ## Functions
9 |
10 | | Function | Description | Parameters | Returns |
11 | | -------------- | -------------------------------------------------------------------------- | -------------------------------------------------- | -------------------------------------------------------------------------------- |
12 | | `getAll` | Retrieves all steps in the stepper. | None | An array of all steps |
13 | | `get` | Retrieves a step by its ID. | `id`: The ID of the step to retrieve | The step with the specified ID |
14 | | `getIndex` | Retrieves the index of a step by its ID. | `id`: The ID of the step to retrieve the index for | The index of the step |
15 | | `getByIndex` | Retrieves a step by its index. | `index`: The index of the step to retrieve | The step at the specified index |
16 | | `getFirst` | Retrieves the first step in the stepper. | None | The first step |
17 | | `getLast` | Retrieves the last step in the stepper. | None | The last step |
18 | | `getNext` | Retrieves the next step after the specified ID. | `id`: The ID of the current step | The next step |
19 | | `getPrev` | Retrieves the previous step before the specified ID. | `id`: The ID of the current step | The previous step |
20 | | `getNeighbors` | Retrieves the neighboring steps (previous and next) of the specified step. | `id`: The ID of the current step | An object containing the previous and next steps, or `null` if they do not exist |
21 |
22 | ## Usage Example
23 |
24 | Here's a quick example of how you might use the `Utils` object in a React component:
25 |
26 | ```tsx twoslash
27 | import * as React from "react";
28 | import { defineStepper } from "@stepperize/react";
29 |
30 | // Define the stepper with the steps
31 | const { utils } = defineStepper(
32 | { id: "step1", name: "Step 1" },
33 | { id: "step2", name: "Step 2" },
34 | { id: "step3", name: "Step 3" },
35 | { id: "step4", name: "Step 4" }
36 | );
37 |
38 | export function StepNavigator() {
39 | // Fetch all steps
40 | const allSteps = utils.getAll(); // Result: Array of all steps
41 |
42 | // Fetch the first and last steps
43 | const firstStep = utils.getFirst(); // Result: { id: 'step1', name: 'Step 1' }
44 | const lastStep = utils.getLast(); // Result: { id: 'step4', name: 'Step 4' }
45 |
46 | // Get a step by ID
47 | const stepById = utils.get("step3"); // Result: { id: 'step3', name: 'Step 3' }
48 |
49 | // Get the index of a step by ID
50 | const indexOfStep2 = utils.getIndex("step2"); // Result: 1
51 |
52 | // Get a step by its index
53 | const stepAtIndex2 = utils.getByIndex(2); // Result: { id: 'step3', name: 'Step 3' }
54 |
55 | // Get the next and previous steps
56 | const nextStepAfter1 = utils.getNext("step1"); // Result: { id: 'step2', name: 'Step 2' }
57 | const prevStepBefore3 = utils.getPrev("step3"); // Result: { id: 'step2', name: 'Step 2' }
58 |
59 | // Get neighbors of a step
60 | const neighborsOfStep2 = utils.getNeighbors("step2");
61 | // Result: { prev: { id: 'step1', name: 'Step 1' }, next: { id: 'step3', name: 'Step 3' } }
62 | }
63 | ```
64 |
--------------------------------------------------------------------------------
/apps/docs/content/docs/vue/api-references/utils.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Utils
3 | description: Pure utility functions for working with steps
4 | ---
5 |
6 | The `Utils` object provides a set of pure functions for working with steps. These functions help you query and traverse step collections without modifying state or requiring stepper context.
7 |
8 | ## Functions
9 |
10 | | Function | Description | Parameters | Returns |
11 | | -------------- | -------------------------------------------------------------------------- | -------------------------------------------------- | -------------------------------------------------------------------------------- |
12 | | `getAll` | Retrieves all steps in the stepper. | None | An array of all steps |
13 | | `get` | Retrieves a step by its ID. | `id`: The ID of the step to retrieve | The step with the specified ID |
14 | | `getIndex` | Retrieves the index of a step by its ID. | `id`: The ID of the step to retrieve the index for | The index of the step |
15 | | `getByIndex` | Retrieves a step by its index. | `index`: The index of the step to retrieve | The step at the specified index |
16 | | `getFirst` | Retrieves the first step in the stepper. | None | The first step |
17 | | `getLast` | Retrieves the last step in the stepper. | None | The last step |
18 | | `getNext` | Retrieves the next step after the specified ID. | `id`: The ID of the current step | The next step |
19 | | `getPrev` | Retrieves the previous step before the specified ID. | `id`: The ID of the current step | The previous step |
20 | | `getNeighbors` | Retrieves the neighboring steps (previous and next) of the specified step. | `id`: The ID of the current step | An object containing the previous and next steps, or `null` if they do not exist |
21 |
22 | ## Usage Example
23 |
24 | Here's a quick example of how you might use the `Utils` object in a Vue component:
25 |
26 | ```tsx
27 | import { defineStepper } from "@stepperize/vue";
28 |
29 | // Step definitions
30 | const steps = [
31 | { id: "step1", name: "Step 1" },
32 | { id: "step2", name: "Step 2" },
33 | { id: "step3", name: "Step 3" },
34 | { id: "step4", name: "Step 4" },
35 | ];
36 |
37 | // Define the stepper with the steps
38 | const { utils } = defineStepper(...steps);
39 |
40 | export function StepNavigator() {
41 | // Fetch all steps
42 | const allSteps = utils.getAll(); // Result: Array of all steps
43 |
44 | // Fetch the first and last steps
45 | const firstStep = utils.getFirst(); // Result: { id: 'step1', name: 'Step 1' }
46 | const lastStep = utils.getLast(); // Result: { id: 'step4', name: 'Step 4' }
47 |
48 | // Get a step by ID
49 | const stepById = utils.get("step3"); // Result: { id: 'step3', name: 'Step 3' }
50 |
51 | // Get the index of a step by ID
52 | const indexOfStep2 = utils.getIndex("step2"); // Result: 1
53 |
54 | // Get a step by its index
55 | const stepAtIndex2 = utils.getByIndex(2); // Result: { id: 'step3', name: 'Step 3' }
56 |
57 | // Get the next and previous steps
58 | const nextStepAfter1 = utils.getNext("step1"); // Result: { id: 'step2', name: 'Step 2' }
59 | const prevStepBefore3 = utils.getPrev("step3"); // Result: { id: 'step2', name: 'Step 2' }
60 |
61 | // Get neighbors of a step
62 | const neighborsOfStep2 = utils.getNeighbors("step2");
63 | // Result: { prev: { id: 'step1', name: 'Step 1' }, next: { id: 'step3', name: 'Step 3' } }
64 | }
65 | ```
66 |
--------------------------------------------------------------------------------
/apps/docs/registry/new-york/ui/form.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import type * as LabelPrimitive from "@radix-ui/react-label"
4 | import { Slot } from "@radix-ui/react-slot"
5 | import * as React from "react"
6 | import {
7 | Controller,
8 | type ControllerProps,
9 | type FieldPath,
10 | type FieldValues,
11 | FormProvider,
12 | useFormContext,
13 | useFormState,
14 | } from "react-hook-form"
15 |
16 | import { cn } from "@/lib/utils"
17 | import { Label } from "@/registry/new-york/ui/label"
18 |
19 | const Form = FormProvider
20 |
21 | type FormFieldContextValue<
22 | TFieldValues extends FieldValues = FieldValues,
23 | TName extends FieldPath = FieldPath,
24 | > = {
25 | name: TName
26 | }
27 |
28 | const FormFieldContext = React.createContext(
29 | {} as FormFieldContextValue
30 | )
31 |
32 | const FormField = <
33 | TFieldValues extends FieldValues = FieldValues,
34 | TName extends FieldPath = FieldPath,
35 | >({
36 | ...props
37 | }: ControllerProps) => {
38 | return (
39 |
40 |
41 |
42 | )
43 | }
44 |
45 | const useFormField = () => {
46 | const fieldContext = React.useContext(FormFieldContext)
47 | const itemContext = React.useContext(FormItemContext)
48 | const { getFieldState } = useFormContext()
49 | const formState = useFormState({ name: fieldContext.name })
50 | const fieldState = getFieldState(fieldContext.name, formState)
51 |
52 | if (!fieldContext) {
53 | throw new Error("useFormField should be used within ")
54 | }
55 |
56 | const { id } = itemContext
57 |
58 | return {
59 | id,
60 | name: fieldContext.name,
61 | formItemId: `${id}-form-item`,
62 | formDescriptionId: `${id}-form-item-description`,
63 | formMessageId: `${id}-form-item-message`,
64 | ...fieldState,
65 | }
66 | }
67 |
68 | type FormItemContextValue = {
69 | id: string
70 | }
71 |
72 | const FormItemContext = React.createContext(
73 | {} as FormItemContextValue
74 | )
75 |
76 | function FormItem({ className, ...props }: React.ComponentProps<"div">) {
77 | const id = React.useId()
78 |
79 | return (
80 |
81 |
86 |
87 | )
88 | }
89 |
90 | function FormLabel({
91 | className,
92 | ...props
93 | }: React.ComponentProps) {
94 | const { error, formItemId } = useFormField()
95 |
96 | return (
97 |
104 | )
105 | }
106 |
107 | function FormControl({ ...props }: React.ComponentProps) {
108 | const { error, formItemId, formDescriptionId, formMessageId } = useFormField()
109 |
110 | return (
111 |
122 | )
123 | }
124 |
125 | function FormDescription({ className, ...props }: React.ComponentProps<"p">) {
126 | const { formDescriptionId } = useFormField()
127 |
128 | return (
129 |
135 | )
136 | }
137 |
138 | function FormMessage({ className, ...props }: React.ComponentProps<"p">) {
139 | const { error, formMessageId } = useFormField()
140 | const body = error ? String(error?.message ?? "") : props.children
141 |
142 | if (!body) {
143 | return null
144 | }
145 |
146 | return (
147 |
153 | {body}
154 |
155 | )
156 | }
157 |
158 | export {
159 | useFormField,
160 | Form,
161 | FormItem,
162 | FormLabel,
163 | FormControl,
164 | FormDescription,
165 | FormMessage,
166 | FormField,
167 | }
168 |
--------------------------------------------------------------------------------
/packages/vue/src/define-stepper.ts:
--------------------------------------------------------------------------------
1 | import type { Get, Metadata, Step, Stepper } from "@stepperize/core";
2 | import {
3 | executeTransition,
4 | generateCommonStepperUseFns,
5 | generateStepperUtils,
6 | getInitialMetadata,
7 | getInitialStepIndex,
8 | updateStepIndex,
9 | } from "@stepperize/core";
10 | import {
11 | type ComputedRef,
12 | type InjectionKey,
13 | type MaybeRefOrGetter,
14 | computed,
15 | defineComponent,
16 | inject,
17 | provide,
18 | ref,
19 | toValue,
20 | watch,
21 | } from "vue";
22 | import type { ScopedProps, StepperReturn } from "./types";
23 |
24 | /**
25 | * Creates a stepper context and utility functions for managing stepper state.
26 | *
27 | * @param steps - The steps to be included in the stepper.
28 | * @returns An object containing the stepper context and utility functions.
29 | */
30 | export const defineStepper = (...steps: Steps): StepperReturn => {
31 | const contextKey = Symbol("StepperizeContext") as InjectionKey>>;
32 |
33 | const utils = generateStepperUtils(...steps);
34 |
35 | const useStepper = (config?: {
36 | initialStep?: MaybeRefOrGetter>;
37 | initialMetadata?: Partial, Metadata>>;
38 | }) => {
39 | const stepIndex = ref(getInitialStepIndex(steps, toValue(config?.initialStep)));
40 | const metadata = ref(getInitialMetadata(steps, toValue(config?.initialMetadata)));
41 |
42 | watch(
43 | () => toValue(config?.initialStep),
44 | (value) => {
45 | stepIndex.value = getInitialStepIndex(steps, value);
46 | },
47 | );
48 |
49 | const current = computed(() => steps[stepIndex.value]);
50 |
51 | const stepper = computed(() => {
52 | const currentStep = current.value;
53 | const currentStepIndex = stepIndex.value;
54 |
55 | const isLast = stepIndex.value === steps.length - 1;
56 | const isFirst = stepIndex.value === 0;
57 |
58 | return {
59 | all: steps,
60 | current: currentStep,
61 | isLast,
62 | isFirst,
63 | metadata: metadata.value,
64 | setMetadata(id, data) {
65 | if (metadata.value[id] === data) return;
66 | metadata.value[id] = data;
67 | },
68 | getMetadata(id) {
69 | return metadata.value[id];
70 | },
71 | resetMetadata(keepInitialMetadata) {
72 | metadata.value = getInitialMetadata(steps, keepInitialMetadata ? config?.initialMetadata : undefined);
73 | },
74 | next() {
75 | updateStepIndex(steps, stepIndex.value + 1, (newIndex) => {
76 | stepIndex.value = newIndex;
77 | });
78 | },
79 | prev() {
80 | updateStepIndex(steps, stepIndex.value - 1, (newIndex) => {
81 | stepIndex.value = newIndex;
82 | });
83 | },
84 | get(id) {
85 | return steps.find((step) => step.id === id);
86 | },
87 | goTo(id) {
88 | const index = steps.findIndex((s) => s.id === id);
89 | if (index === -1) throw new Error(`Step with id "${id}" not found.`);
90 | updateStepIndex(steps, index, (newIndex) => {
91 | stepIndex.value = newIndex;
92 | });
93 | },
94 | reset() {
95 | stepIndex.value = getInitialStepIndex(steps, toValue(config?.initialStep));
96 | },
97 | async beforeNext(callback) {
98 | await executeTransition({ stepper: stepper.value, direction: "next", callback, before: true });
99 | },
100 | async afterNext(callback) {
101 | this.next();
102 | await executeTransition({ stepper: stepper.value, direction: "next", callback, before: false });
103 | },
104 | async beforePrev(callback) {
105 | await executeTransition({ stepper: stepper.value, direction: "prev", callback, before: true });
106 | },
107 | async afterPrev(callback) {
108 | this.prev();
109 | await executeTransition({ stepper: stepper.value, direction: "prev", callback, before: false });
110 | },
111 | async beforeGoTo(id, callback) {
112 | await executeTransition({ stepper: stepper.value, direction: "goTo", callback, before: true, targetId: id });
113 | },
114 | async afterGoTo(id, callback) {
115 | this.goTo(id);
116 | await executeTransition({ stepper: stepper.value, direction: "goTo", callback, before: false, targetId: id });
117 | },
118 | ...generateCommonStepperUseFns(steps, currentStep, currentStepIndex),
119 | } as Stepper;
120 | });
121 |
122 | return stepper;
123 | };
124 |
125 | return {
126 | steps,
127 | utils,
128 | Scoped: defineComponent>((props, { slots }) => {
129 | provide(contextKey, useStepper(props));
130 | return () => slots.default?.();
131 | }),
132 | useStepper(props = {}) {
133 | return inject(contextKey) ?? useStepper(props);
134 | },
135 | };
136 | };
137 |
--------------------------------------------------------------------------------
/apps/docs/components/banner.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import { type HTMLAttributes, useEffect, useState } from 'react';
4 | import { X } from 'lucide-react';
5 | import { cn } from '../lib/cn';
6 | import { buttonVariants } from './ui/button';
7 |
8 | export function Banner({
9 | id,
10 | variant = 'normal',
11 | changeLayout = true,
12 | height = '3rem',
13 | ...props
14 | }: HTMLAttributes & {
15 | /**
16 | * @defaultValue 3rem
17 | */
18 | height?: string;
19 |
20 | /**
21 | * @defaultValue 'normal'
22 | */
23 | variant?: 'rainbow' | 'normal';
24 |
25 | /**
26 | * Change Fumadocs layout styles
27 | *
28 | * @defaultValue true
29 | */
30 | changeLayout?: boolean;
31 | }) {
32 | const [open, setOpen] = useState(true);
33 | const globalKey = id ? `nd-banner-${id}` : null;
34 |
35 | useEffect(() => {
36 | if (globalKey) setOpen(localStorage.getItem(globalKey) !== 'true');
37 | }, [globalKey]);
38 |
39 | if (!open) return null;
40 |
41 | return (
42 |
55 | {changeLayout && open ? (
56 |
61 | ) : null}
62 | {globalKey ? (
63 |
64 | ) : null}
65 | {globalKey ? (
66 |
71 | ) : null}
72 |
73 | {variant === 'rainbow' ? rainbowLayer : null}
74 | {props.children}
75 | {id ? (
76 | {
80 | setOpen(false);
81 | if (globalKey) localStorage.setItem(globalKey, 'true');
82 | }}
83 | className={cn(
84 | buttonVariants({
85 | color: 'ghost',
86 | className:
87 | 'absolute end-2 top-1/2 -translate-y-1/2 text-fd-muted-foreground',
88 | size: 'icon',
89 | }),
90 | )}
91 | >
92 |
93 |
94 | ) : null}
95 |
96 | );
97 | }
98 |
99 | const maskImage =
100 | 'linear-gradient(to bottom,white,transparent), radial-gradient(circle at top center, white, transparent)';
101 |
102 | const rainbowLayer = (
103 | <>
104 |
123 |
141 |
147 | >
148 | );
149 |
--------------------------------------------------------------------------------
/packages/solid/src/define-stepper.ts:
--------------------------------------------------------------------------------
1 | import type { Get, Metadata, Step, Stepper } from "@stepperize/core";
2 | import {
3 | executeTransition,
4 | generateCommonStepperUseFns,
5 | generateStepperUtils,
6 | getInitialMetadata,
7 | getInitialStepIndex,
8 | updateStepIndex,
9 | } from "@stepperize/core";
10 | import { createMemo } from "solid-js";
11 | import { createStore, produce } from "solid-js/store";
12 | import type { StepperReturn } from "./types";
13 |
14 | /**
15 | * Creates a stepper context and utility functions for managing stepper state.
16 | *
17 | * @param steps - The steps to be included in the stepper.
18 | * @returns An object containing the stepper context and utility functions.
19 | */
20 | export const defineStepper = (...steps: Steps): StepperReturn => {
21 | const utils = generateStepperUtils(...steps);
22 |
23 | const [state, setState] = createStore({
24 | stepIndex: 0,
25 | metadata: getInitialMetadata(steps, undefined),
26 | });
27 |
28 | const useStepper = (config?: {
29 | initialStep?: Get.Id;
30 | initialMetadata?: Partial, Metadata>>;
31 | }) => {
32 | const { initialStep, initialMetadata } = config ?? {};
33 | const initialStepIndex = getInitialStepIndex(steps, initialStep);
34 |
35 | if (initialStep || initialMetadata) {
36 | setState(
37 | produce((state) => {
38 | state.stepIndex = initialStepIndex;
39 | state.metadata = getInitialMetadata(steps, initialMetadata);
40 | }),
41 | );
42 | }
43 |
44 | const currentStep = createMemo(() => steps[state.stepIndex]);
45 |
46 | const stepper = {
47 | all: steps,
48 | get current() {
49 | return currentStep();
50 | },
51 | get isLast() {
52 | return state.stepIndex === steps.length - 1;
53 | },
54 | get isFirst() {
55 | return state.stepIndex === 0;
56 | },
57 | metadata: state.metadata,
58 | setMetadata(id, data) {
59 | if (state.metadata[id] === data) return;
60 | setState(
61 | produce((state) => {
62 | state.metadata[id] = data;
63 | }),
64 | );
65 | },
66 | getMetadata(id) {
67 | return state.metadata[id];
68 | },
69 | resetMetadata(keepInitialMetadata) {
70 | const newMetadata = getInitialMetadata(steps, keepInitialMetadata ? initialMetadata : undefined);
71 | setState(
72 | produce((state) => {
73 | state.metadata = newMetadata;
74 | }),
75 | );
76 | },
77 | next() {
78 | updateStepIndex(steps, state.stepIndex + 1, (newIndex) => {
79 | setState(
80 | produce((state) => {
81 | state.stepIndex = newIndex;
82 | }),
83 | );
84 | });
85 | },
86 | prev() {
87 | updateStepIndex(steps, state.stepIndex - 1, (newIndex) => {
88 | setState(
89 | produce((state) => {
90 | state.stepIndex = newIndex;
91 | }),
92 | );
93 | });
94 | },
95 | get(id) {
96 | return steps.find((step) => step.id === id);
97 | },
98 | goTo(id) {
99 | const index = steps.findIndex((s) => s.id === id);
100 | if (index === -1) throw new Error(`Step with id "${id}" not found.`);
101 | updateStepIndex(steps, index, (newIndex) => {
102 | setState(
103 | produce((state) => {
104 | state.stepIndex = newIndex;
105 | }),
106 | );
107 | });
108 | },
109 | reset() {
110 | updateStepIndex(steps, getInitialStepIndex(steps, initialStep), (newIndex) => {
111 | setState(
112 | produce((state) => {
113 | state.stepIndex = newIndex;
114 | }),
115 | );
116 | });
117 | },
118 | async beforeNext(callback) {
119 | await executeTransition({ stepper, direction: "next", callback, before: true });
120 | },
121 | async afterNext(callback) {
122 | this.next();
123 | await executeTransition({ stepper, direction: "next", callback, before: false });
124 | },
125 | async beforePrev(callback) {
126 | await executeTransition({ stepper, direction: "prev", callback, before: true });
127 | },
128 | async afterPrev(callback) {
129 | this.prev();
130 | await executeTransition({ stepper, direction: "prev", callback, before: false });
131 | },
132 | async beforeGoTo(id, callback) {
133 | await executeTransition({ stepper, direction: "goTo", callback, before: true, targetId: id });
134 | },
135 | async afterGoTo(id, callback) {
136 | this.goTo(id);
137 | await executeTransition({ stepper, direction: "goTo", callback, before: false, targetId: id });
138 | },
139 | when(...args) {
140 | return generateCommonStepperUseFns(steps, currentStep(), state.stepIndex).when(...args);
141 | },
142 | switch(...args) {
143 | return generateCommonStepperUseFns(steps, currentStep(), state.stepIndex).switch(...args);
144 | },
145 | match(...args) {
146 | return generateCommonStepperUseFns(steps, currentStep(), state.stepIndex).match(...args);
147 | },
148 | } as Stepper;
149 |
150 | return stepper;
151 | };
152 |
153 | return {
154 | steps,
155 | utils,
156 | useStepper,
157 | };
158 | };
159 |
--------------------------------------------------------------------------------
/apps/docs/app/docs/[[...slug]]/page.tsx:
--------------------------------------------------------------------------------
1 | import { DemoViewer } from "@/components/demo-viewer";
2 | import { source } from "@/lib/source";
3 | import { getPageTreePeers } from "fumadocs-core/server";
4 | import { Popup, PopupContent, PopupTrigger } from "fumadocs-twoslash/ui";
5 | import { Card } from "fumadocs-ui/components/card";
6 | import { Cards } from "fumadocs-ui/components/card";
7 | import { CodeBlock } from "fumadocs-ui/components/codeblock";
8 | import { Pre } from "fumadocs-ui/components/codeblock";
9 | import { Step, Steps } from "fumadocs-ui/components/steps";
10 | import { Tab, Tabs } from "fumadocs-ui/components/tabs";
11 | import defaultMdxComponents from "fumadocs-ui/mdx";
12 | import {
13 | DocsBody,
14 | DocsDescription,
15 | DocsPage,
16 | DocsTitle,
17 | } from "fumadocs-ui/page";
18 |
19 | import { StepperDemo } from "@/registry/new-york/blocks/stepper-demo/components/stepper-demo";
20 | import { StepperWithDescription } from "@/registry/new-york/blocks/stepper-with-description/components/stepper-with-description";
21 | import { StepperWithForm } from "@/registry/new-york/blocks/stepper-with-form/components/stepper-with-form";
22 | import { StepperWithIcon } from "@/registry/new-york/blocks/stepper-with-icon/components/stepper-with-icon";
23 | import { StepperWithLabelOrientation } from "@/registry/new-york/blocks/stepper-with-label-orientation/components/stepper-with-label-orientation";
24 | import { StepperWithResponsiveVariant } from "@/registry/new-york/blocks/stepper-with-responsive-variant/components/stepper-with-responsive-variant";
25 | import { StepperWithTracking } from "@/registry/new-york/blocks/stepper-with-tracking/components/stepper-with-tracking";
26 | import { StepperWithVariants } from "@/registry/new-york/blocks/stepper-with-variants/components/stepper-with-variants";
27 |
28 | import { OpenInV0 } from "@/components/open-in-v0";
29 |
30 | import { createMetadata } from "@/lib/metadata";
31 | import type { Metadata } from "next";
32 | import { notFound } from "next/navigation";
33 |
34 | export default async function Page(props: {
35 | params: Promise<{ slug?: string[] }>;
36 | }) {
37 | const params = await props.params;
38 | const page = source.getPage(params.slug);
39 | if (!page) notFound();
40 |
41 | const MDX = page.data.body;
42 |
43 | const path = `apps/docs/content/docs/${page.file.path}`;
44 |
45 | return (
46 |
60 | {page.data.title}
61 | {page.data.description}
62 |
63 | (
67 |
68 | {props.children}
69 |
70 | ),
71 | Popup,
72 | PopupContent,
73 | PopupTrigger,
74 | Tabs,
75 | Tab,
76 | Steps,
77 | Step,
78 | DemoViewer,
79 | StepperDemo,
80 | StepperWithDescription,
81 | StepperWithForm,
82 | StepperWithIcon,
83 | StepperWithLabelOrientation,
84 | StepperWithResponsiveVariant,
85 | StepperWithTracking,
86 | StepperWithVariants,
87 | OpenInV0,
88 | DocsCategory: ({ url }: { url: string }) => (
89 |
90 | ),
91 | }}
92 | />
93 |
94 |
95 | );
96 | }
97 |
98 | function DocsCategory({ url }: { url: string }) {
99 | return (
100 |
101 | {getPageTreePeers(source.pageTree, url).map((peer) => (
102 |
103 | {peer.description}
104 |
105 | ))}
106 |
107 | );
108 | }
109 |
110 | export async function generateMetadata(props: {
111 | params: Promise<{ slug: string[] }>;
112 | }): Promise {
113 | const { slug = [] } = await props.params;
114 | const page = source.getPage(slug);
115 | if (!page) notFound();
116 |
117 | const description =
118 | page.data.description ?? "The library for building stepper components";
119 |
120 | const baseUrl = process.env.NEXT_PUBLIC_BASE_URL ?? "http://localhost:3000";
121 |
122 | const image = {
123 | url: `${baseUrl}/og/${slug.join("/")}/image.png`,
124 | width: 1200,
125 | height: 630,
126 | };
127 |
128 | return createMetadata({
129 | title: page.data.title,
130 | description,
131 | openGraph: {
132 | url: `${baseUrl}/docs/${page.slugs.join("/")}`,
133 | images: [image],
134 | },
135 | twitter: {
136 | images: [image],
137 | },
138 | });
139 | }
140 |
141 | export function generateStaticParams() {
142 | return source.generateParams();
143 | }
144 |
--------------------------------------------------------------------------------