@dsbtek/component-library
v0.1.6
Published
Advanced components built with shadcn/ui
Readme
@dsbtek/component-library
A collection of advanced React components built with TypeScript, Tailwind CSS, and shadcn/ui.
Table of Contents
Installation
- Install the package:
npm install @dsbtek/component-library- Install peer dependencies:
npm install react@^18 react-dom@^18- Set up Tailwind CSS:
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p- Update your
tailwind.config.js:
/** @type {import('tailwindcss').Config} */
module.exports = {
darkMode: ['class'],
content: [
'./src/**/*.{js,ts,jsx,tsx}',
'./node_modules/@dsbtek/component-library/**/*.{js,ts,jsx,tsx}',
],
theme: {
extend: {
colors: {
border: 'hsl(var(--border))',
input: 'hsl(var(--input))',
ring: 'hsl(var(--ring))',
background: 'hsl(var(--background))',
foreground: 'hsl(var(--foreground))',
primary: {
DEFAULT: 'hsl(var(--primary))',
foreground: 'hsl(var(--primary-foreground))',
},
secondary: {
DEFAULT: 'hsl(var(--secondary))',
foreground: 'hsl(var(--secondary-foreground))',
},
destructive: {
DEFAULT: 'hsl(var(--destructive))',
foreground: 'hsl(var(--destructive-foreground))',
},
muted: {
DEFAULT: 'hsl(var(--muted))',
foreground: 'hsl(var(--muted-foreground))',
},
accent: {
DEFAULT: 'hsl(var(--accent))',
foreground: 'hsl(var(--accent-foreground))',
},
popover: {
DEFAULT: 'hsl(var(--popover))',
foreground: 'hsl(var(--popover-foreground))',
},
card: {
DEFAULT: 'hsl(var(--card))',
foreground: 'hsl(var(--card-foreground))',
},
},
borderRadius: {
lg: 'var(--radius)',
md: 'calc(var(--radius) - 2px)',
sm: 'calc(var(--radius) - 4px)',
},
},
},
plugins: [require('tailwindcss-animate')],
};- Add these CSS variables to your global CSS:
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--card: 0 0% 100%;
--card-foreground: 222.2 84% 4.9%;
--popover: 0 0% 100%;
--popover-foreground: 222.2 84% 4.9%;
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 40% 98%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--ring: 222.2 84% 4.9%;
--radius: 0.5rem;
/* Sidebar Variables */
--sidebar-background: 0 0% 98%;
--sidebar-foreground: 240 5.3% 26.1%;
--sidebar-primary: 240 5.9% 10%;
--sidebar-primary-foreground: 0 0% 98%;
--sidebar-accent: 240 4.8% 95.9%;
--sidebar-accent-foreground: 240 5.9% 10%;
--sidebar-border: 220 13% 91%;
--sidebar-ring: 217.2 91.2% 59.8%;
}
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
--card: 222.2 84% 4.9%;
--card-foreground: 210 40% 98%;
--popover: 222.2 84% 4.9%;
--popover-foreground: 210 40% 98%;
--primary: 210 40% 98%;
--primary-foreground: 222.2 47.4% 11.2%;
--secondary: 217.2 32.6% 17.5%;
--secondary-foreground: 210 40% 98%;
--muted: 217.2 32.6% 17.5%;
--muted-foreground: 215 20.2% 65.1%;
--accent: 217.2 32.6% 17.5%;
--accent-foreground: 210 40% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 210 40% 98%;
--border: 217.2 32.6% 17.5%;
--input: 217.2 32.6% 17.5%;
--ring: 212.7 26.8% 83.9%;
/* Sidebar Variables - Dark Mode */
--sidebar-background: 240 5.9% 10%;
--sidebar-foreground: 240 4.8% 95.9%;
--sidebar-primary: 0 0% 98%;
--sidebar-primary-foreground: 240 5.9% 10%;
--sidebar-accent: 240 3.7% 15.9%;
--sidebar-accent-foreground: 240 4.8% 95.9%;
--sidebar-border: 240 3.7% 15.9%;
--sidebar-ring: 217.2 91.2% 59.8%;
}
}Components
AppSidebar
A flexible, customizable sidebar component with navigation, search, team switching, and user profile features. Built on top of the shadcn/ui Sidebar primitives.
Props
| Prop | Type | Default | Description
|-----|-----|-----|-----
| teams | Team[] | [] | Array of teams for the team switcher
| defaultTeam | Team | - | Default selected team
| onTeamChange | (team: Team) => void | - | Callback when team selection changes
| onSearch | (query: string) => void | - | Callback when search query changes
| searchPlaceholder | string | "Search..." | Placeholder text for search input
| navItems | NavItem[] | Required | Array of navigation items
| navGroupLabel | string | "Navigation" | Label for the main navigation group
| navSecondary | { title: string; url: string; icon: LucideIcon; onClick?: () => void }[] | [] | Array of secondary navigation items
| navSecondaryLabel | string | - | Label for the secondary navigation group
| user | User | - | User data for the profile section
| onLogout | () => void | - | Callback when logout is clicked
| userMenuItems | { group: string; items: { label: string; icon: React.ComponentType<{ className?: string }>; onClick?: () => void }[] }[] | - | Custom menu items for user dropdown
| isLoading | boolean | false | Loading state for all sidebar components
| variant | "sidebar" | "floating" | "inset" | "sidebar" | The variant of the sidebar
| collapsible | "offcanvas" | "icon" | "none" | "icon" | Collapsible state of the sidebar
| side | "left" | "right" | "left" | The side of the sidebar
| defaultOpen | boolean | true | Default open state of the sidebar
| open | boolean | - | Controlled open state
| onOpenChange | (open: boolean) => void | - | Callback when open state changes
| ...props | React.ComponentProps<typeof Sidebar> | - | All props from shadcn Sidebar component
SidebarProvider
The SidebarProvider is a context provider that manages the state of the sidebar. It's automatically included in the AppSidebar component, but can also be used separately for more advanced use cases.
Props
| Prop | Type | Default | Description
|-----|-----|-----|-----
| children | React.ReactNode | Required | Child components
| defaultOpen | boolean | true | Default open state of the sidebar
| open | boolean | - | Controlled open state
| onOpenChange | (open: boolean) => void | - | Callback when open state changes
useSidebar Hook
The useSidebar hook provides access to the sidebar context. It returns an object with the following properties:
| Property | Type | Description
|-----|-----|-----|-----
| state | "expanded" | "collapsed" | Current state of the sidebar
| open | boolean | Whether the sidebar is open
| setOpen | (open: boolean) => void | Function to set the open state
| openMobile | boolean | Whether the mobile sidebar is open
| setOpenMobile | (open: boolean) => void | Function to set the mobile open state
| isMobile | boolean | Whether the current device is mobile
| toggleSidebar | () => void | Function to toggle the sidebar open/closed
Example Usage
import { AppSidebar } from '@dsbtek/component-library';
import { Home, Settings, Users, HelpCircle, LogOut } from 'lucide-react';
function SidebarExample() {
const navItems = [
{
title: "Dashboard",
url: "/dashboard",
icon: Home,
isActive: true
},
{
title: "Settings",
url: "/settings",
icon: Settings,
items: [
{ title: "Profile", url: "/settings/profile" },
{ title: "Account", url: "/settings/account" },
{ title: "Preferences", url: "/settings/preferences" }
]
},
{
title: "Users",
url: "/users",
icon: Users
}
];
const teams = [
{ name: "Acme Inc", logo: Users, plan: "Pro" },
{ name: "Personal", logo: Home, plan: "Free" }
];
const secondaryNav = [
{ title: "Help", url: "/help", icon: HelpCircle }
];
const user = {
name: "John Doe",
email: "[email protected]",
image: "/avatar.jpg"
};
return (
<div className="flex h-screen">
<AppSidebar
teams={teams}
defaultTeam={teams[0]}
onTeamChange={(team) => console.log("Team changed:", team)}
onSearch={(query) => console.log("Search:", query)}
searchPlaceholder="Search..."
navItems={navItems}
navGroupLabel="Main Navigation"
navSecondary={secondaryNav}
navSecondaryLabel="Support"
user={user}
onLogout={() => console.log("Logout clicked")}
userMenuItems={[
{
group: "Account",
items: [
{
label: "Settings",
icon: Settings,
onClick: () => console.log("Settings clicked")
},
{
label: "Logout",
icon: LogOut,
onClick: () => console.log("Logout clicked")
}
]
}
]}
defaultOpen={true}
collapsible="icon"
variant="sidebar"
/>
<main className="flex-1 p-6">
<h1 className="text-2xl font-bold">Dashboard</h1>
{/* Your page content */}
</main>
</div>
);
}Using SidebarProvider Separately
For more advanced use cases, you can use the SidebarProvider and sidebar components separately:
import {
SidebarProvider,
useSidebar,
NavProjects,
NavMain,
NavUser
} from '@dsbtek/component-library';
import { Home, Settings, Users } from 'lucide-react';
function CustomSidebar() {
const navItems = [
{ title: "Dashboard", url: "/dashboard", icon: Home },
{ title: "Settings", url: "/settings", icon: Settings },
{ title: "Users", url: "/users", icon: Users }
];
const projects = [
{ name: "Project A", url: "/projects/a", icon: Home },
{ name: "Project B", url: "/projects/b", icon: Settings }
];
const user = {
name: "John Doe",
email: "[email protected]"
};
return (
<SidebarProvider defaultOpen={true}>
<div className="flex h-screen">
<div className="w-64 bg-sidebar text-sidebar-foreground">
<NavMain items={navItems} groupLabel="Navigation" />
<NavProjects projects={projects} groupLabel="Projects" />
<NavUser user={user} onLogout={() => console.log("Logout")} />
</div>
<main className="flex-1 p-6">
<SidebarToggleButton />
<h1 className="text-2xl font-bold">Dashboard</h1>
{/* Your page content */}
</main>
</div>
</SidebarProvider>
);
}
// Custom toggle button using the useSidebar hook
function SidebarToggleButton() {
const { toggleSidebar, state } = useSidebar();
return (
<button
onClick={toggleSidebar}
className="p-2 rounded-md bg-primary text-primary-foreground"
>
{state === "expanded" ? "Collapse" : "Expand"} Sidebar
</button>
);
}Breadcrumbs
A navigation component that helps users understand their current location within a website's hierarchy.
Props
| Prop | Type | Default | Description
|-----|-----|-----|-----
| segments | Array<{ label: string; href?: string }> | Required | Array of breadcrumb segments
| separator | React.ReactNode | <ChevronRight className="h-4 w-4" /> | Custom separator between breadcrumb items
| className | string | - | Additional CSS classes
| onNavigate | (href: string) => void | - | Callback function when a breadcrumb is clicked
Example Usage
import { Breadcrumbs } from '@dsbtek/component-library';
function BreadcrumbsExample() {
return (
<Breadcrumbs
segments={[
{ label: 'Home', href: '/' },
{ label: 'Products', href: '/products' },
{ label: 'Electronics', href: '/products/electronics' },
{ label: 'Smartphones' },
]}
onNavigate={(href) => console.log(`Navigating to: ${href}`)}
/>
);
}ColorPicker
A comprehensive color selection component with RGB, HSL support, color schemes, and history.
Props
| Prop | Type | Default | Description
|-----|-----|-----|-----
| color | string | #000000 | Current color value in hex format
| onChange | (value: string) => void | - | Callback function when color changes
| className | string | - | Additional CSS classes
Example Usage
import { ColorPicker } from '@dsbtek/component-library';
import { useState } from 'react';
function ColorPickerExample() {
const [color, setColor] = useState('#3B82F6');
return (
<ColorPicker
color={color}
onChange={setColor}
/>
);
}DataTable
A feature-rich table component with sorting, filtering, pagination, and more.
Props
| Prop | Type | Default | Description
|-----|-----|-----|-----
| data | T[] | Required | Array of data items
| columns | ColumnDef<T>[] | Required | Array of column definitions
| meta | { current_page: number; last_page: number; per_page: number; total: number } | - | Pagination metadata
| loading | boolean | false | Loading state of the table
| onPageChange | (page: number) => void | - | Callback when page changes
| onPerPageChange | (perPage: number) => void | - | Callback when items per page changes
| onSort | (column: string, direction: 'asc' | 'desc' | null) => void | - | Callback when sorting changes
| onSearch | (value: string) => void | - | Callback when search value changes
| onFilter | (filters: AdvancedFilter[]) => void | - | Callback when filters change
| pageSizeOptions | number[] | [10,20,50] | Available options for items per page
| renderItemActions | (row: T) => React.ReactNode | - | Function to render action buttons for each row
| features | Partial<DataTableFeatures> | - | Object to enable/disable various table features
Example Usage
import { DataTable } from '@dsbtek/component-library';
function DataTableExample() {
const columns = [
{ accessorKey: 'name', header: 'Name' },
{ accessorKey: 'email', header: 'Email' },
{ accessorKey: 'role', header: 'Role' },
];
const data = [
{ id: '1', name: 'John Doe', email: '[email protected]', role: 'Admin' },
{ id: '2', name: 'Jane Smith', email: '[email protected]', role: 'User' },
// ... more data
];
return (
<DataTable
data={data}
columns={columns}
features={{
sorting: true,
pagination: true,
search: true,
}}
onPageChange={(page) => console.log(`Page changed to ${page}`)}
onSort={(column, direction) => console.log(`Sorting ${column} ${direction}`)}
/>
);
}DateTimePicker
A versatile date and time selection component with support for ranges, time-only, and date-only modes.
Props
| Prop | Type | Default | Description
|-----|-----|-----|-----
| date | DateValue | DateRange | undefined | null | - | Selected date(s)
| setDate | (date: DateValue | DateRange | undefined) => void | Required | Callback function when date changes
| isRange | boolean | false | Enable date range selection
| includeTime | boolean | true | Include time selection
| dateOnly | boolean | false | Show date picker only
| timeOnly | boolean | false | Show time picker only
| minDate | Date | - | Minimum selectable date
| maxDate | Date | - | Maximum selectable date
| disabledDates | Date[] | - | Array of disabled dates
| clearable | boolean | true | Allow clearing the selection
| disabled | boolean | false | Disable the input
Example Usage
import { DateTimePicker } from '@dsbtek/component-library';
import { useState } from 'react';
function DateTimePickerExample() {
const [date, setDate] = useState<Date | undefined>(new Date());
return (
<DateTimePicker
date={date}
setDate={setDate}
includeTime={true}
/>
);
}FileInput
A file upload component with drag and drop support, previews, and progress indication.
Props
| Prop | Type | Default | Description
|-----|-----|-----|-----
| value | FileWithPreview[] | - | Array of selected files
| onChange | (files: FileWithPreview[]) => void | Required | Callback when files change
| multiple | boolean | false | Allow multiple file selection
| accept | Record<string, string[]> | { "image/*": [".png", ".jpg", ".jpeg", ".gif"], "application/pdf": [".pdf"] } | Accepted file types
| maxSize | number | 2MB | Maximum file size in bytes
| maxFiles | number | 5 | Maximum number of files
| disabled | boolean | false | Disable the input
| loading | boolean | false | Show loading state
| progress | number | number[] | - | Upload progress percentage
| onRemove | (file: FileWithPreview) => void | - | Callback when a file is removed
Example Usage
import { FileInput } from '@dsbtek/component-library';
import { useState } from 'react';
function FileInputExample() {
const [files, setFiles] = useState([]);
return (
<FileInput
value={files}
onChange={setFiles}
multiple={true}
maxSize={5 * 1024 * 1024} // 5MB
/>
);
}MultiSelect
A flexible select component that supports single or multiple selection with grouping, search, and responsive behavior.
Props
| Prop | Type | Default | Description
|-----|-----|-----|-----
| options | OptionType[] | Required | Array of selectable options with format {label: string, value: string, group?: string}
| selected | string[] | Required | Array of selected option values
| onChange | (value: string[]) => void | Required | Callback when selection changes
| placeholder | string | "Select..." | Placeholder text
| className | string | - | Additional CSS classes for the main component
| multiple | boolean | false | Allow multiple selection
| onLoadMore | () => void | - | Callback for infinite loading
| hasMore | boolean | false | Indicates if more options can be loaded
| isDialog | boolean | false | Use dialog instead of drawer on mobile
| triggerClassName | string | - | Custom class for the trigger button
| contentClassName | string | - | Custom class for the dropdown content
| badgeClassName | string | - | Custom class for the selected item badges
| commandClassName | string | - | Custom class for the command component
| commandInputClassName | string | - | Custom class for the search input
| commandListClassName | string | - | Custom class for the options list
| commandItemClassName | string | - | Custom class for individual option items
| commandEmptyClassName | string | - | Custom class for the empty state
| commandGroupClassName | string | - | Custom class for option groups
| commandSeparatorClassName | string | - | Custom class for group separators
| align | "start" \| "center" \| "end" | "start" | Alignment of the dropdown relative to the trigger
| sideOffset | number | 5 | Offset from the trigger
| width | "auto" \| "trigger" \| string | "trigger" | Width of the dropdown content
Example Usage
import { MultiSelect } from '@dsbtek/component-library';
import { useState } from 'react';
function MultiSelectExample() {
const [selected, setSelected] = useState(['react']);
const options = [
{ label: 'React', value: 'react', group: 'Frontend' },
{ label: 'Vue', value: 'vue', group: 'Frontend' },
{ label: 'Angular', value: 'angular', group: 'Frontend' },
{ label: 'Node.js', value: 'nodejs', group: 'Backend' },
{ label: 'Express', value: 'express', group: 'Backend' },
];
return (
<MultiSelect
options={options}
selected={selected}
onChange={setSelected}
placeholder="Select technologies..."
multiple={true}
className="w-[350px]"
badgeClassName="bg-blue-100 text-blue-800"
/>
);
}MultiStepper
A comprehensive multi-step form component with navigation, validation, and customizable steps.
Props
| Prop | Type | Default | Description
|-----|-----|-----|-----
| context | React.Context<UseMultiStepFormTypeOptions<T>> | Required | Context created with buildMultiStepForm
| previousLabel | string | "Previous" | Label for the previous button
| nextLabel | string | "Next" | Label for the next button
| endStepLabel | string | "Submit" | Label for the submit button
| showNavbar | boolean | true | Show the step navigation bar
| showButtons | boolean | true | Show the navigation buttons
| isLoading | boolean | false | Loading state for the submit button
| className | string | "" | Additional CSS classes
| debug | boolean | false | Show debug information
Utility Functions
| Function | Description |-----|----- | buildMultiStepForm | Creates a context and provider for a multi-step form | useMultiStepForm | Hook for accessing multi-step form functionality
Example Usage
import {
buildMultiStepForm,
useMultiStepForm,
Form
} from '@dsbtek/component-library';
import { z } from 'zod';
import { useForm, FormProvider, useFormContext } from 'react-hook-form';
import {
FormField,
FormItem,
FormLabel,
FormControl,
FormMessage
} from '@/components/ui/form';
import { Input } from '@/components/ui/input';
import { PasswordInput } from '@dsbtek/component-library';
import { MultiSelect } from '@dsbtek/component-library';
import { FileInput } from '@dsbtek/component-library';
// Define your form schema
export const signupSchema = z.object({
name: z.string().min(2, "Name must be at least 2 characters"),
email: z.string().email("Please enter a valid email"),
phone: z.string().min(10, "Please enter a valid phone number"),
password: z.string().min(8, "Password must be at least 8 characters"),
password_confirmation: z.string(),
roles: z.array(z.string()),
permissions: z.array(z.string()),
image: z.any().optional(),
}).refine((data) => data.password === data.password_confirmation, {
message: "Passwords do not match",
path: ["password_confirmation"],
});
// Step 1: Basic Information
export function Step1() {
const { control } = useFormContext();
return (
<>
<FormField
control={control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>Name</FormLabel>
<FormControl>
<Input placeholder="Enter your name" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input placeholder="Enter your email" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={control}
name="phone"
render={({ field }) => (
<FormItem>
<FormLabel>Phone</FormLabel>
<FormControl>
<Input placeholder="Enter your phone number" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</>
);
}
// Step 2: Security
export function Step2() {
const { control } = useFormContext();
return (
<>
<FormField
control={control}
name="password"
render={({ field }) => (
<FormItem>
<FormLabel>Password</FormLabel>
<FormControl>
<PasswordInput placeholder="Enter your password" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={control}
name="password_confirmation"
render={({ field }) => (
<FormItem>
<FormLabel>Confirm Password</FormLabel>
<FormControl>
<PasswordInput placeholder="Confirm your password" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</>
);
}
// Step 3: Roles & Permissions
export function Step3() {
const { control } = useFormContext();
const roleOptions = [
{ label: 'Admin', value: 'admin' },
{ label: 'User', value: 'user' },
{ label: 'Editor', value: 'editor' },
];
const permissionOptions = [
{ label: 'Create', value: 'create', group: 'Content' },
{ label: 'Edit', value: 'edit', group: 'Content' },
{ label: 'Delete', value: 'delete', group: 'Content' },
{ label: 'View Users', value: 'view_users', group: 'Users' },
{ label: 'Manage Users', value: 'manage_users', group: 'Users' },
];
return (
<>
<FormField
control={control}
name="roles"
render={({ field }) => (
<FormItem>
<FormLabel>Roles</FormLabel>
<FormControl>
<MultiSelect
options={roleOptions}
selected={field.value || []}
onChange={field.onChange}
placeholder="Select roles"
multiple={true}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={control}
name="permissions"
render={({ field }) => (
<FormItem>
<FormLabel>Permissions</FormLabel>
<FormControl>
<MultiSelect
options={permissionOptions}
selected={field.value || []}
onChange={field.onChange}
placeholder="Select permissions"
multiple={true}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</>
);
}
// Step 4: Profile Picture
export function Step4() {
const { control } = useFormContext();
return (
<FormField
control={control}
name="image"
render={({ field }) => (
<FormItem>
<FormLabel>Profile Picture</FormLabel>
<FormControl>
<FileInput
value={field.value || []}
onChange={field.onChange}
accept={{ "image/*": [".png", ".jpg", ".jpeg"] }}
maxSize={2 * 1024 * 1024} // 2MB
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
);
}//Sign Up Step Config
import React from 'react'
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle
} from '@/components/ui/card'
import { Social } from '@/components/auth/social'
import { BackButton } from '@/components/auth/back-button'
import { cn } from "@/lib/utils"
interface AuthWrapperProps {
children: React.ReactNode
headerTitle: string
headerDescription: string
backButtonLabel: string
backLinkLabel: string
backButtonHref: string
enableSocial: boolean
onSocialLogin: (provider: string) => void
showGoogle?: boolean
showFacebook?: boolean
showApple?: boolean
showGithub?: boolean
showSlack?: boolean
showX?: boolean
showLinkedin?: boolean
showMicrosoft?: boolean
className?: string
contentClassName?: string
width?: string;
}
export function AuthWrapper({
children,
headerTitle,
headerDescription,
backButtonLabel,
backLinkLabel,
backButtonHref,
enableSocial,
onSocialLogin,
className,
contentClassName,
width = "w-[350px]",
...socialProps
}: AuthWrapperProps) {
return (
<Card className={cn(width, className)}>
<CardHeader className="space-y-1">
<CardTitle>{headerTitle}</CardTitle>
<CardDescription>{headerDescription}</CardDescription>
</CardHeader>
<CardContent className={cn(contentClassName)}>{children}</CardContent>
<CardFooter className="flex flex-col items-center gap-4">
{enableSocial && (
<Social onSocialLogin={onSocialLogin} {...socialProps} />
)}
<BackButton
backLinkLabel={backLinkLabel}
label={backButtonLabel}
href={backButtonHref}
/>
</CardFooter>
</Card>
)
}// Use the Sign Up Dorm
"use client";
import {containerSignUpForm as container} from "@/constants/framer-motion";
import {useMultiStepForm, MultiStepNavbar, MultiStepNavButtons} from "@smartflowssbu/component-pack";
import {motion} from "framer-motion";
import MultiStepForm from "@/app/(auth)/signup/_components/multi-step-form";
import {SignUpFormContext} from "@/app/(auth)/signup/_components/signup-step-config";
export default function SignUpForm() {
const {CurrentForm} = useMultiStepForm(SignUpFormContext);
return (
<MultiStepForm title="Sign up" description="Create an account to get started">
<MultiStepNavbar context={SignUpFormContext}/>
<div className="flex-1">
<motion.div variants={container} className="space-y-6" initial="hidden" animate="visible" exit="exit">
<CurrentForm/>
</motion.div>
<MultiStepNavButtons
context={SignUpFormContext}
previousLabel="Previous"
nextLabel="Next"
endStepLabel="Submit"
/>
</div>
</MultiStepForm>
);
};//Use the Multi Step Form
"use client";
import {containerMultiStepForm as container} from "@/constants/framer-motion";
import {useMultiStepForm} from "@/hooks/multi-step-form";
import {motion} from "framer-motion";
import {Form} from "@/components/ui/form";
import React, {PropsWithChildren} from "react";
import {SignUpFormContext} from "@/app/(auth)/signup/_components/signup-step-config";
import {AuthWrapper} from "@/components/auth/auth-wrapper";
interface Props extends PropsWithChildren {
title: string;
description: string;
}
const MultiStepForm = ({title, description, children}: Props) => {
const {form, onSubmit, onErrors} = useMultiStepForm(SignUpFormContext);
function handleSocialLogin(provider: string) {
// Implement social signin logic here
console.log(Social login with ${provider})
}
return (
<Form {...form}>
<form onSubmit={form?.handleSubmit(onSubmit, onErrors)}>
<motion.div
variants={container}
className="flex flex-col gap-2"
initial="hidden"
animate="visible"
exit="exit"
>
<AuthWrapper
headerTitle={title}
headerDescription={description}
backButtonLabel='Already have an account?'
backLinkLabel='Sign in'
backButtonHref='/signin'
enableSocial={false}
onSocialLogin={handleSocialLogin}
width='w-[500px]'
contentClassName='flex flex-col sm:flex-row gap-2 p-2 m-2'>
{children}
</AuthWrapper>
</motion.div>
</form>
</Form>
);
};
export default MultiStepForm;//Use Auth Wrapper
import React from 'react'
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle
} from '@/components/ui/card'
import { Social } from '@/components/auth/social'
import { BackButton } from '@/components/auth/back-button'
import { cn } from "@/lib/utils"
interface AuthWrapperProps {
children: React.ReactNode
headerTitle: string
headerDescription: string
backButtonLabel: string
backLinkLabel: string
backButtonHref: string
enableSocial: boolean
onSocialLogin: (provider: string) => void
showGoogle?: boolean
showFacebook?: boolean
showApple?: boolean
showGithub?: boolean
showSlack?: boolean
showX?: boolean
showLinkedin?: boolean
showMicrosoft?: boolean
className?: string
contentClassName?: string
width?: string;
}
export function AuthWrapper({
children,
headerTitle,
headerDescription,
backButtonLabel,
backLinkLabel,
backButtonHref,
enableSocial,
onSocialLogin,
className,
contentClassName,
width = "w-[350px]",
...socialProps
}: AuthWrapperProps) {
return (
<Card className={cn(width, className)}>
<CardHeader className="space-y-1">
<CardTitle>{headerTitle}</CardTitle>
<CardDescription>{headerDescription}</CardDescription>
</CardHeader>
<CardContent className={cn(contentClassName)}>{children}</CardContent>
<CardFooter className="flex flex-col items-center gap-4">
{enableSocial && (
<Social onSocialLogin={onSocialLogin} {...socialProps} />
)}
<BackButton
backLinkLabel={backLinkLabel}
label={backButtonLabel}
href={backButtonHref}
/>
</CardFooter>
</Card>
)
}//Multi Ster SignUp Form
"use client";
import {SignUpProvider} from "@/app/(auth)/signup/_components/signup-step-config";
import SignUpForm from "@/app/(auth)/signup/_components/form/signup-form";
export default function MultiStepSignUp () {
return (
<SignUpProvider>
<SignUpForm />
</SignUpProvider>
);
};//back button
import Link from 'next/link'
interface BackButtonProps {
label: string
href: string
backLinkLabel: string
}
export function BackButton({ backLinkLabel, label, href }: BackButtonProps) {
return (
<div className="text-sm text-muted-foreground text-center w-full">
{label}{' '}
<Link href={href} className="text-primary underline-offset-4 hover:underline">
{backLinkLabel}
</Link>
</div>
)
}PasswordInput
A password input component with show/hide functionality.
Props
| Prop | Type | Default | Description
|-----|-----|-----|-----
| className | string | - | Additional CSS classes
| showPasswordLabel | string | "Show password" | Screen reader label for show password button
| hidePasswordLabel | string | "Hide password" | Screen reader label for hide password button
| ...props | React.InputHTMLAttributes<HTMLInputElement> | - | All standard input props
Example Usage
import { PasswordInput } from '@smartflowssbu/component-pack';
import { useState } from 'react';
function PasswordInputExample() {
const [password, setPassword] = useState('');
return (
<PasswordInput
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Enter your password"
/>
);
}PhoneInput
An international phone number input with country selection and validation.
Props
| Prop | Type | Default | Description
|-----|-----|-----|-----
| value | string | - | Phone number value
| defaultCountry | CountryCode | "US" | Default country code
| ...props | React.ComponentPropsWithoutRef<'input'> | - | All standard input props
Example Usage
import { PhoneInput, getPhoneData } from '@smartflowssbu/component-pack';
import { useState, useEffect } from 'react';
function PhoneInputExample() {
const [phone, setPhone] = useState('+1');
const [phoneData, setPhoneData] = useState(getPhoneData(phone));
useEffect(() => {
setPhoneData(getPhoneData(phone));
}, [phone]);
return (
<PhoneInput
value={phone}
onChange={(e) => setPhone(e.target.value)}
defaultCountry="US"
/>
);
}ResponsiveAlertDialog
An alert dialog component that adapts to desktop (modal) and mobile (drawer) views.
Props
| Prop | Type | Default | Description
|-----|-----|-----|-----
| trigger | React.ReactNode | Required | Element that triggers the dialog
| title | string | Required | Dialog title
| description | string | Required | Dialog description
| cancelText | string | "Cancel" | Text for the cancel button
| confirmText | string | "Continue" | Text for the confirm button
| onConfirm | () => void | Required | Callback when confirm button is clicked
Example Usage
import { ResponsiveAlertDialog } from '@smartflowssbu/component-pack';
import { Button } from '@smartflowssbu/component-pack';
function AlertDialogExample() {
const handleDelete = () => {
console.log('Item deleted');
};
return (
<ResponsiveAlertDialog
trigger={<Button variant="destructive">Delete Item</Button>}
title="Are you sure?"
description="This action cannot be undone. This will permanently delete the item."
cancelText="Cancel"
confirmText="Delete"
onConfirm={handleDelete}
/>
);
}ResponsiveDialog
A dialog component that adapts to desktop (modal) and mobile (drawer) views.
Props
| Prop | Type | Default | Description
|-----|-----|-----|-----
| trigger | React.ReactNode | Required | Element that triggers the dialog
| title | string | Required | Dialog title
| description | string | Required | Dialog description
| children | React.ReactNode | Required | Dialog content
Example Usage
import { ResponsiveDialog } from '@smartflowssbu/component-pack';
import { Button } from '@smartflowssbu/component-pack';
function DialogExample() {
return (
<ResponsiveDialog
trigger={<Button>Open Dialog</Button>}
title="Edit Profile"
description="Make changes to your profile here."
>
<form className="space-y-4 pt-4">
<div className="space-y-2">
<label htmlFor="name">Name</label>
<input id="name" className="w-full p-2 border rounded" />
</div>
<div className="space-y-2">
<label htmlFor="email">Email</label>
<input id="email" type="email" className="w-full p-2 border rounded" />
</div>
<div className="flex justify-end space-x-2">
<Button type="button" variant="outline">Cancel</Button>
<Button type="submit">Save</Button>
</div>
</form>
</ResponsiveDialog>
);
}TagInput
A component for adding and managing tags with suggestions and validation.
Props
| Prop | Type | Default | Description
|-----|-----|-----|-----
| placeholder | string | "Add tag..." | Placeholder text
| tags | string[] | Required | Array of current tags
| setTags | (tags: string[]) => void | Required | Callback when tags change
| suggestions | string[] | [] | Array of tag suggestions
| maxTags | number | - | Maximum number of tags allowed
| disabled | boolean | false | Disable the input
| error | string | - | Error message to display
Example Usage
import { TagInput } from '@smartflowssbu/component-pack';
import { useState } from 'react';
function TagInputExample() {
const [tags, setTags] = useState(['react', 'typescript']);
const suggestions = [
'react', 'vue', 'angular', 'svelte', 'javascript',
'typescript', 'node', 'express', 'mongodb'
];
return (
<TagInput
tags={tags}
setTags={setTags}
suggestions={suggestions}
placeholder="Add technologies..."
/>
);
}Styling
All components use Tailwind CSS for styling and can be customized using Tailwind classes. The components also respect the theme variables defined in your CSS.
Custom Styling Example
<Breadcrumbs
className="text-blue-600 dark:text-blue-400"
segments={[
{ label: 'Home', href: '/' },
{ label: 'Products', href: '/products' },
{ label: 'Current Page' },
]}
/>
<ColorPicker
className="w-[300px] rounded-lg"
color="#3B82F6"
onChange={setColor}
/>TypeScript Support
All components include full TypeScript support out of the box. Type definitions are automatically available when you import components.
Contributing
Please read our Contributing Guide before submitting a Pull Request.
License
MIT © Smartflowtech Team
