@dsbtek/component-library 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1400 -0
- package/dist/index.d.mts +1153 -0
- package/dist/index.d.ts +1153 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +96 -0
package/README.md
ADDED
|
@@ -0,0 +1,1400 @@
|
|
|
1
|
+
# @smartflowssbu/component-pack
|
|
2
|
+
|
|
3
|
+
A collection of advanced React components built with TypeScript, Tailwind CSS, and shadcn/ui.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Installation](#installation)
|
|
8
|
+
- [Components](#components)
|
|
9
|
+
- [AppSidebar](#appsidebar)
|
|
10
|
+
- [Breadcrumbs](#breadcrumbs)
|
|
11
|
+
- [ColorPicker](#colorpicker)
|
|
12
|
+
- [DataTable](#datatable)
|
|
13
|
+
- [DateTimePicker](#datetimepicker)
|
|
14
|
+
- [FileInput](#fileinput)
|
|
15
|
+
- [MultiSelect](#multiselect)
|
|
16
|
+
- [MultiStepper](#multistepper)
|
|
17
|
+
- [PasswordInput](#passwordinput)
|
|
18
|
+
- [PhoneInput](#phoneinput)
|
|
19
|
+
- [ResponsiveAlertDialog](#responsivealertdialog)
|
|
20
|
+
- [ResponsiveDialog](#responsivedialog)
|
|
21
|
+
- [TagInput](#taginput)
|
|
22
|
+
- [Styling](#styling)
|
|
23
|
+
- [Contributing](#contributing)
|
|
24
|
+
- [License](#license)
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
1. Install the package:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install @smartflowssbu/component-pack
|
|
32
|
+
````
|
|
33
|
+
|
|
34
|
+
2. Install peer dependencies:
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npm install react@^18 react-dom@^18
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
3. Set up Tailwind CSS:
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
npm install -D tailwindcss postcss autoprefixer
|
|
46
|
+
npx tailwindcss init -p
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
4. Update your `tailwind.config.js`:
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
```javascript
|
|
53
|
+
/** @type {import('tailwindcss').Config} */
|
|
54
|
+
module.exports = {
|
|
55
|
+
darkMode: ['class'],
|
|
56
|
+
content: [
|
|
57
|
+
'./src/**/*.{js,ts,jsx,tsx}',
|
|
58
|
+
'./node_modules/@smartflowssbu/component-pack/**/*.{js,ts,jsx,tsx}',
|
|
59
|
+
],
|
|
60
|
+
theme: {
|
|
61
|
+
extend: {
|
|
62
|
+
colors: {
|
|
63
|
+
border: 'hsl(var(--border))',
|
|
64
|
+
input: 'hsl(var(--input))',
|
|
65
|
+
ring: 'hsl(var(--ring))',
|
|
66
|
+
background: 'hsl(var(--background))',
|
|
67
|
+
foreground: 'hsl(var(--foreground))',
|
|
68
|
+
primary: {
|
|
69
|
+
DEFAULT: 'hsl(var(--primary))',
|
|
70
|
+
foreground: 'hsl(var(--primary-foreground))',
|
|
71
|
+
},
|
|
72
|
+
secondary: {
|
|
73
|
+
DEFAULT: 'hsl(var(--secondary))',
|
|
74
|
+
foreground: 'hsl(var(--secondary-foreground))',
|
|
75
|
+
},
|
|
76
|
+
destructive: {
|
|
77
|
+
DEFAULT: 'hsl(var(--destructive))',
|
|
78
|
+
foreground: 'hsl(var(--destructive-foreground))',
|
|
79
|
+
},
|
|
80
|
+
muted: {
|
|
81
|
+
DEFAULT: 'hsl(var(--muted))',
|
|
82
|
+
foreground: 'hsl(var(--muted-foreground))',
|
|
83
|
+
},
|
|
84
|
+
accent: {
|
|
85
|
+
DEFAULT: 'hsl(var(--accent))',
|
|
86
|
+
foreground: 'hsl(var(--accent-foreground))',
|
|
87
|
+
},
|
|
88
|
+
popover: {
|
|
89
|
+
DEFAULT: 'hsl(var(--popover))',
|
|
90
|
+
foreground: 'hsl(var(--popover-foreground))',
|
|
91
|
+
},
|
|
92
|
+
card: {
|
|
93
|
+
DEFAULT: 'hsl(var(--card))',
|
|
94
|
+
foreground: 'hsl(var(--card-foreground))',
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
borderRadius: {
|
|
98
|
+
lg: 'var(--radius)',
|
|
99
|
+
md: 'calc(var(--radius) - 2px)',
|
|
100
|
+
sm: 'calc(var(--radius) - 4px)',
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
plugins: [require('tailwindcss-animate')],
|
|
105
|
+
};
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
5. Add these CSS variables to your global CSS:
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
```css
|
|
112
|
+
@tailwind base;
|
|
113
|
+
@tailwind components;
|
|
114
|
+
@tailwind utilities;
|
|
115
|
+
|
|
116
|
+
@layer base {
|
|
117
|
+
:root {
|
|
118
|
+
--background: 0 0% 100%;
|
|
119
|
+
--foreground: 222.2 84% 4.9%;
|
|
120
|
+
--card: 0 0% 100%;
|
|
121
|
+
--card-foreground: 222.2 84% 4.9%;
|
|
122
|
+
--popover: 0 0% 100%;
|
|
123
|
+
--popover-foreground: 222.2 84% 4.9%;
|
|
124
|
+
--primary: 222.2 47.4% 11.2%;
|
|
125
|
+
--primary-foreground: 210 40% 98%;
|
|
126
|
+
--secondary: 210 40% 96.1%;
|
|
127
|
+
--secondary-foreground: 222.2 47.4% 11.2%;
|
|
128
|
+
--muted: 210 40% 96.1%;
|
|
129
|
+
--muted-foreground: 215.4 16.3% 46.9%;
|
|
130
|
+
--accent: 210 40% 96.1%;
|
|
131
|
+
--accent-foreground: 222.2 47.4% 11.2%;
|
|
132
|
+
--destructive: 0 84.2% 60.2%;
|
|
133
|
+
--destructive-foreground: 210 40% 98%;
|
|
134
|
+
--border: 214.3 31.8% 91.4%;
|
|
135
|
+
--input: 214.3 31.8% 91.4%;
|
|
136
|
+
--ring: 222.2 84% 4.9%;
|
|
137
|
+
--radius: 0.5rem;
|
|
138
|
+
|
|
139
|
+
/* Sidebar Variables */
|
|
140
|
+
--sidebar-background: 0 0% 98%;
|
|
141
|
+
--sidebar-foreground: 240 5.3% 26.1%;
|
|
142
|
+
--sidebar-primary: 240 5.9% 10%;
|
|
143
|
+
--sidebar-primary-foreground: 0 0% 98%;
|
|
144
|
+
--sidebar-accent: 240 4.8% 95.9%;
|
|
145
|
+
--sidebar-accent-foreground: 240 5.9% 10%;
|
|
146
|
+
--sidebar-border: 220 13% 91%;
|
|
147
|
+
--sidebar-ring: 217.2 91.2% 59.8%;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.dark {
|
|
151
|
+
--background: 222.2 84% 4.9%;
|
|
152
|
+
--foreground: 210 40% 98%;
|
|
153
|
+
--card: 222.2 84% 4.9%;
|
|
154
|
+
--card-foreground: 210 40% 98%;
|
|
155
|
+
--popover: 222.2 84% 4.9%;
|
|
156
|
+
--popover-foreground: 210 40% 98%;
|
|
157
|
+
--primary: 210 40% 98%;
|
|
158
|
+
--primary-foreground: 222.2 47.4% 11.2%;
|
|
159
|
+
--secondary: 217.2 32.6% 17.5%;
|
|
160
|
+
--secondary-foreground: 210 40% 98%;
|
|
161
|
+
--muted: 217.2 32.6% 17.5%;
|
|
162
|
+
--muted-foreground: 215 20.2% 65.1%;
|
|
163
|
+
--accent: 217.2 32.6% 17.5%;
|
|
164
|
+
--accent-foreground: 210 40% 98%;
|
|
165
|
+
--destructive: 0 62.8% 30.6%;
|
|
166
|
+
--destructive-foreground: 210 40% 98%;
|
|
167
|
+
--border: 217.2 32.6% 17.5%;
|
|
168
|
+
--input: 217.2 32.6% 17.5%;
|
|
169
|
+
--ring: 212.7 26.8% 83.9%;
|
|
170
|
+
|
|
171
|
+
/* Sidebar Variables - Dark Mode */
|
|
172
|
+
--sidebar-background: 240 5.9% 10%;
|
|
173
|
+
--sidebar-foreground: 240 4.8% 95.9%;
|
|
174
|
+
--sidebar-primary: 0 0% 98%;
|
|
175
|
+
--sidebar-primary-foreground: 240 5.9% 10%;
|
|
176
|
+
--sidebar-accent: 240 3.7% 15.9%;
|
|
177
|
+
--sidebar-accent-foreground: 240 4.8% 95.9%;
|
|
178
|
+
--sidebar-border: 240 3.7% 15.9%;
|
|
179
|
+
--sidebar-ring: 217.2 91.2% 59.8%;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Components
|
|
185
|
+
|
|
186
|
+
### AppSidebar
|
|
187
|
+
|
|
188
|
+
A flexible, customizable sidebar component with navigation, search, team switching, and user profile features. Built on top of the shadcn/ui Sidebar primitives.
|
|
189
|
+
|
|
190
|
+
#### Props
|
|
191
|
+
|
|
192
|
+
| Prop | Type | Default | Description
|
|
193
|
+
|-----|-----|-----|-----
|
|
194
|
+
| teams | `Team[]` | `[]` | Array of teams for the team switcher
|
|
195
|
+
| defaultTeam | `Team` | - | Default selected team
|
|
196
|
+
| onTeamChange | `(team: Team) => void` | - | Callback when team selection changes
|
|
197
|
+
| onSearch | `(query: string) => void` | - | Callback when search query changes
|
|
198
|
+
| searchPlaceholder | `string` | `"Search..."` | Placeholder text for search input
|
|
199
|
+
| navItems | `NavItem[]` | Required | Array of navigation items
|
|
200
|
+
| navGroupLabel | `string` | `"Navigation"` | Label for the main navigation group
|
|
201
|
+
| navSecondary | `{ title: string; url: string; icon: LucideIcon; onClick?: () => void }[]` | `[]` | Array of secondary navigation items
|
|
202
|
+
| navSecondaryLabel | `string` | - | Label for the secondary navigation group
|
|
203
|
+
| user | `User` | - | User data for the profile section
|
|
204
|
+
| onLogout | `() => void` | - | Callback when logout is clicked
|
|
205
|
+
| userMenuItems | `{ group: string; items: { label: string; icon: React.ComponentType<{ className?: string }>; onClick?: () => void }[] }[]` | - | Custom menu items for user dropdown
|
|
206
|
+
| isLoading | `boolean` | `false` | Loading state for all sidebar components
|
|
207
|
+
| variant | `"sidebar" | "floating" | "inset"` | `"sidebar"` | The variant of the sidebar
|
|
208
|
+
| collapsible | `"offcanvas" | "icon" | "none"` | `"icon"` | Collapsible state of the sidebar
|
|
209
|
+
| side | `"left" | "right"` | `"left"` | The side of the sidebar
|
|
210
|
+
| defaultOpen | `boolean` | `true` | Default open state of the sidebar
|
|
211
|
+
| open | `boolean` | - | Controlled open state
|
|
212
|
+
| onOpenChange | `(open: boolean) => void` | - | Callback when open state changes
|
|
213
|
+
| ...props | `React.ComponentProps<typeof Sidebar>` | - | All props from shadcn Sidebar component
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
#### SidebarProvider
|
|
217
|
+
|
|
218
|
+
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.
|
|
219
|
+
|
|
220
|
+
##### Props
|
|
221
|
+
|
|
222
|
+
| Prop | Type | Default | Description
|
|
223
|
+
|-----|-----|-----|-----
|
|
224
|
+
| children | `React.ReactNode` | Required | Child components
|
|
225
|
+
| defaultOpen | `boolean` | `true` | Default open state of the sidebar
|
|
226
|
+
| open | `boolean` | - | Controlled open state
|
|
227
|
+
| onOpenChange | `(open: boolean) => void` | - | Callback when open state changes
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
##### useSidebar Hook
|
|
231
|
+
|
|
232
|
+
The `useSidebar` hook provides access to the sidebar context. It returns an object with the following properties:
|
|
233
|
+
|
|
234
|
+
| Property | Type | Description
|
|
235
|
+
|-----|-----|-----|-----
|
|
236
|
+
| state | `"expanded" | "collapsed"` | Current state of the sidebar
|
|
237
|
+
| open | `boolean` | Whether the sidebar is open
|
|
238
|
+
| setOpen | `(open: boolean) => void` | Function to set the open state
|
|
239
|
+
| openMobile | `boolean` | Whether the mobile sidebar is open
|
|
240
|
+
| setOpenMobile | `(open: boolean) => void` | Function to set the mobile open state
|
|
241
|
+
| isMobile | `boolean` | Whether the current device is mobile
|
|
242
|
+
| toggleSidebar | `() => void` | Function to toggle the sidebar open/closed
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
#### Example Usage
|
|
246
|
+
|
|
247
|
+
```tsx
|
|
248
|
+
import { AppSidebar } from '@smartflowssbu/component-pack';
|
|
249
|
+
import { Home, Settings, Users, HelpCircle, LogOut } from 'lucide-react';
|
|
250
|
+
|
|
251
|
+
function SidebarExample() {
|
|
252
|
+
const navItems = [
|
|
253
|
+
{
|
|
254
|
+
title: "Dashboard",
|
|
255
|
+
url: "/dashboard",
|
|
256
|
+
icon: Home,
|
|
257
|
+
isActive: true
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
title: "Settings",
|
|
261
|
+
url: "/settings",
|
|
262
|
+
icon: Settings,
|
|
263
|
+
items: [
|
|
264
|
+
{ title: "Profile", url: "/settings/profile" },
|
|
265
|
+
{ title: "Account", url: "/settings/account" },
|
|
266
|
+
{ title: "Preferences", url: "/settings/preferences" }
|
|
267
|
+
]
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
title: "Users",
|
|
271
|
+
url: "/users",
|
|
272
|
+
icon: Users
|
|
273
|
+
}
|
|
274
|
+
];
|
|
275
|
+
|
|
276
|
+
const teams = [
|
|
277
|
+
{ name: "Acme Inc", logo: Users, plan: "Pro" },
|
|
278
|
+
{ name: "Personal", logo: Home, plan: "Free" }
|
|
279
|
+
];
|
|
280
|
+
|
|
281
|
+
const secondaryNav = [
|
|
282
|
+
{ title: "Help", url: "/help", icon: HelpCircle }
|
|
283
|
+
];
|
|
284
|
+
|
|
285
|
+
const user = {
|
|
286
|
+
name: "John Doe",
|
|
287
|
+
email: "john@example.com",
|
|
288
|
+
image: "/avatar.jpg"
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
return (
|
|
292
|
+
<div className="flex h-screen">
|
|
293
|
+
<AppSidebar
|
|
294
|
+
teams={teams}
|
|
295
|
+
defaultTeam={teams[0]}
|
|
296
|
+
onTeamChange={(team) => console.log("Team changed:", team)}
|
|
297
|
+
onSearch={(query) => console.log("Search:", query)}
|
|
298
|
+
searchPlaceholder="Search..."
|
|
299
|
+
navItems={navItems}
|
|
300
|
+
navGroupLabel="Main Navigation"
|
|
301
|
+
navSecondary={secondaryNav}
|
|
302
|
+
navSecondaryLabel="Support"
|
|
303
|
+
user={user}
|
|
304
|
+
onLogout={() => console.log("Logout clicked")}
|
|
305
|
+
userMenuItems={[
|
|
306
|
+
{
|
|
307
|
+
group: "Account",
|
|
308
|
+
items: [
|
|
309
|
+
{
|
|
310
|
+
label: "Settings",
|
|
311
|
+
icon: Settings,
|
|
312
|
+
onClick: () => console.log("Settings clicked")
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
label: "Logout",
|
|
316
|
+
icon: LogOut,
|
|
317
|
+
onClick: () => console.log("Logout clicked")
|
|
318
|
+
}
|
|
319
|
+
]
|
|
320
|
+
}
|
|
321
|
+
]}
|
|
322
|
+
defaultOpen={true}
|
|
323
|
+
collapsible="icon"
|
|
324
|
+
variant="sidebar"
|
|
325
|
+
/>
|
|
326
|
+
<main className="flex-1 p-6">
|
|
327
|
+
<h1 className="text-2xl font-bold">Dashboard</h1>
|
|
328
|
+
{/* Your page content */}
|
|
329
|
+
</main>
|
|
330
|
+
</div>
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
#### Using SidebarProvider Separately
|
|
336
|
+
|
|
337
|
+
For more advanced use cases, you can use the `SidebarProvider` and sidebar components separately:
|
|
338
|
+
|
|
339
|
+
```tsx
|
|
340
|
+
import {
|
|
341
|
+
SidebarProvider,
|
|
342
|
+
useSidebar,
|
|
343
|
+
NavProjects,
|
|
344
|
+
NavMain,
|
|
345
|
+
NavUser
|
|
346
|
+
} from '@smartflowssbu/component-pack';
|
|
347
|
+
import { Home, Settings, Users } from 'lucide-react';
|
|
348
|
+
|
|
349
|
+
function CustomSidebar() {
|
|
350
|
+
const navItems = [
|
|
351
|
+
{ title: "Dashboard", url: "/dashboard", icon: Home },
|
|
352
|
+
{ title: "Settings", url: "/settings", icon: Settings },
|
|
353
|
+
{ title: "Users", url: "/users", icon: Users }
|
|
354
|
+
];
|
|
355
|
+
|
|
356
|
+
const projects = [
|
|
357
|
+
{ name: "Project A", url: "/projects/a", icon: Home },
|
|
358
|
+
{ name: "Project B", url: "/projects/b", icon: Settings }
|
|
359
|
+
];
|
|
360
|
+
|
|
361
|
+
const user = {
|
|
362
|
+
name: "John Doe",
|
|
363
|
+
email: "john@example.com"
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
return (
|
|
367
|
+
<SidebarProvider defaultOpen={true}>
|
|
368
|
+
<div className="flex h-screen">
|
|
369
|
+
<div className="w-64 bg-sidebar text-sidebar-foreground">
|
|
370
|
+
<NavMain items={navItems} groupLabel="Navigation" />
|
|
371
|
+
<NavProjects projects={projects} groupLabel="Projects" />
|
|
372
|
+
<NavUser user={user} onLogout={() => console.log("Logout")} />
|
|
373
|
+
</div>
|
|
374
|
+
<main className="flex-1 p-6">
|
|
375
|
+
<SidebarToggleButton />
|
|
376
|
+
<h1 className="text-2xl font-bold">Dashboard</h1>
|
|
377
|
+
{/* Your page content */}
|
|
378
|
+
</main>
|
|
379
|
+
</div>
|
|
380
|
+
</SidebarProvider>
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// Custom toggle button using the useSidebar hook
|
|
385
|
+
function SidebarToggleButton() {
|
|
386
|
+
const { toggleSidebar, state } = useSidebar();
|
|
387
|
+
|
|
388
|
+
return (
|
|
389
|
+
<button
|
|
390
|
+
onClick={toggleSidebar}
|
|
391
|
+
className="p-2 rounded-md bg-primary text-primary-foreground"
|
|
392
|
+
>
|
|
393
|
+
{state === "expanded" ? "Collapse" : "Expand"} Sidebar
|
|
394
|
+
</button>
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
### Breadcrumbs
|
|
400
|
+
|
|
401
|
+
A navigation component that helps users understand their current location within a website's hierarchy.
|
|
402
|
+
|
|
403
|
+
#### Props
|
|
404
|
+
|
|
405
|
+
| Prop | Type | Default | Description
|
|
406
|
+
|-----|-----|-----|-----
|
|
407
|
+
| segments | `Array<{ label: string; href?: string }>` | Required | Array of breadcrumb segments
|
|
408
|
+
| separator | `React.ReactNode` | `<ChevronRight className="h-4 w-4" />` | Custom separator between breadcrumb items
|
|
409
|
+
| className | `string` | - | Additional CSS classes
|
|
410
|
+
| onNavigate | `(href: string) => void` | - | Callback function when a breadcrumb is clicked
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
#### Example Usage
|
|
414
|
+
|
|
415
|
+
```tsx
|
|
416
|
+
import { Breadcrumbs } from '@smartflowssbu/component-pack';
|
|
417
|
+
|
|
418
|
+
function BreadcrumbsExample() {
|
|
419
|
+
return (
|
|
420
|
+
<Breadcrumbs
|
|
421
|
+
segments={[
|
|
422
|
+
{ label: 'Home', href: '/' },
|
|
423
|
+
{ label: 'Products', href: '/products' },
|
|
424
|
+
{ label: 'Electronics', href: '/products/electronics' },
|
|
425
|
+
{ label: 'Smartphones' },
|
|
426
|
+
]}
|
|
427
|
+
onNavigate={(href) => console.log(`Navigating to: ${href}`)}
|
|
428
|
+
/>
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
### ColorPicker
|
|
434
|
+
|
|
435
|
+
A comprehensive color selection component with RGB, HSL support, color schemes, and history.
|
|
436
|
+
|
|
437
|
+
#### Props
|
|
438
|
+
|
|
439
|
+
| Prop | Type | Default | Description
|
|
440
|
+
|-----|-----|-----|-----
|
|
441
|
+
| color | `string` | `#000000` | Current color value in hex format
|
|
442
|
+
| onChange | `(value: string) => void` | - | Callback function when color changes
|
|
443
|
+
| className | `string` | - | Additional CSS classes
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
#### Example Usage
|
|
447
|
+
|
|
448
|
+
```tsx
|
|
449
|
+
import { ColorPicker } from '@smartflowssbu/component-pack';
|
|
450
|
+
import { useState } from 'react';
|
|
451
|
+
|
|
452
|
+
function ColorPickerExample() {
|
|
453
|
+
const [color, setColor] = useState('#3B82F6');
|
|
454
|
+
|
|
455
|
+
return (
|
|
456
|
+
<ColorPicker
|
|
457
|
+
color={color}
|
|
458
|
+
onChange={setColor}
|
|
459
|
+
/>
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
### DataTable
|
|
465
|
+
|
|
466
|
+
A feature-rich table component with sorting, filtering, pagination, and more.
|
|
467
|
+
|
|
468
|
+
#### Props
|
|
469
|
+
|
|
470
|
+
| Prop | Type | Default | Description
|
|
471
|
+
|-----|-----|-----|-----
|
|
472
|
+
| data | `T[]` | Required | Array of data items
|
|
473
|
+
| columns | `ColumnDef<T>[]` | Required | Array of column definitions
|
|
474
|
+
| meta | `{ current_page: number; last_page: number; per_page: number; total: number }` | - | Pagination metadata
|
|
475
|
+
| loading | `boolean` | `false` | Loading state of the table
|
|
476
|
+
| onPageChange | `(page: number) => void` | - | Callback when page changes
|
|
477
|
+
| onPerPageChange | `(perPage: number) => void` | - | Callback when items per page changes
|
|
478
|
+
| onSort | `(column: string, direction: 'asc' | 'desc' | null) => void` | - | Callback when sorting changes
|
|
479
|
+
| onSearch | `(value: string) => void` | - | Callback when search value changes
|
|
480
|
+
| onFilter | `(filters: AdvancedFilter[]) => void` | - | Callback when filters change
|
|
481
|
+
| pageSizeOptions | `number[]` | `[10,20,50]` | Available options for items per page
|
|
482
|
+
| renderItemActions | `(row: T) => React.ReactNode` | - | Function to render action buttons for each row
|
|
483
|
+
| features | `Partial<DataTableFeatures>` | - | Object to enable/disable various table features
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
#### Example Usage
|
|
487
|
+
|
|
488
|
+
```tsx
|
|
489
|
+
import { DataTable } from '@smartflowssbu/component-pack';
|
|
490
|
+
|
|
491
|
+
function DataTableExample() {
|
|
492
|
+
const columns = [
|
|
493
|
+
{ accessorKey: 'name', header: 'Name' },
|
|
494
|
+
{ accessorKey: 'email', header: 'Email' },
|
|
495
|
+
{ accessorKey: 'role', header: 'Role' },
|
|
496
|
+
];
|
|
497
|
+
|
|
498
|
+
const data = [
|
|
499
|
+
{ id: '1', name: 'John Doe', email: 'john@example.com', role: 'Admin' },
|
|
500
|
+
{ id: '2', name: 'Jane Smith', email: 'jane@example.com', role: 'User' },
|
|
501
|
+
// ... more data
|
|
502
|
+
];
|
|
503
|
+
|
|
504
|
+
return (
|
|
505
|
+
<DataTable
|
|
506
|
+
data={data}
|
|
507
|
+
columns={columns}
|
|
508
|
+
features={{
|
|
509
|
+
sorting: true,
|
|
510
|
+
pagination: true,
|
|
511
|
+
search: true,
|
|
512
|
+
}}
|
|
513
|
+
onPageChange={(page) => console.log(`Page changed to ${page}`)}
|
|
514
|
+
onSort={(column, direction) => console.log(`Sorting ${column} ${direction}`)}
|
|
515
|
+
/>
|
|
516
|
+
);
|
|
517
|
+
}
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
### DateTimePicker
|
|
521
|
+
|
|
522
|
+
A versatile date and time selection component with support for ranges, time-only, and date-only modes.
|
|
523
|
+
|
|
524
|
+
#### Props
|
|
525
|
+
|
|
526
|
+
| Prop | Type | Default | Description
|
|
527
|
+
|-----|-----|-----|-----
|
|
528
|
+
| date | `DateValue | DateRange | undefined | null` | - | Selected date(s)
|
|
529
|
+
| setDate | `(date: DateValue | DateRange | undefined) => void` | Required | Callback function when date changes
|
|
530
|
+
| isRange | `boolean` | `false` | Enable date range selection
|
|
531
|
+
| includeTime | `boolean` | `true` | Include time selection
|
|
532
|
+
| dateOnly | `boolean` | `false` | Show date picker only
|
|
533
|
+
| timeOnly | `boolean` | `false` | Show time picker only
|
|
534
|
+
| minDate | `Date` | - | Minimum selectable date
|
|
535
|
+
| maxDate | `Date` | - | Maximum selectable date
|
|
536
|
+
| disabledDates | `Date[]` | - | Array of disabled dates
|
|
537
|
+
| clearable | `boolean` | `true` | Allow clearing the selection
|
|
538
|
+
| disabled | `boolean` | `false` | Disable the input
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
#### Example Usage
|
|
542
|
+
|
|
543
|
+
```tsx
|
|
544
|
+
import { DateTimePicker } from '@smartflowssbu/component-pack';
|
|
545
|
+
import { useState } from 'react';
|
|
546
|
+
|
|
547
|
+
function DateTimePickerExample() {
|
|
548
|
+
const [date, setDate] = useState<Date | undefined>(new Date());
|
|
549
|
+
|
|
550
|
+
return (
|
|
551
|
+
<DateTimePicker
|
|
552
|
+
date={date}
|
|
553
|
+
setDate={setDate}
|
|
554
|
+
includeTime={true}
|
|
555
|
+
/>
|
|
556
|
+
);
|
|
557
|
+
}
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
### FileInput
|
|
561
|
+
|
|
562
|
+
A file upload component with drag and drop support, previews, and progress indication.
|
|
563
|
+
|
|
564
|
+
#### Props
|
|
565
|
+
|
|
566
|
+
| Prop | Type | Default | Description
|
|
567
|
+
|-----|-----|-----|-----
|
|
568
|
+
| value | `FileWithPreview[]` | - | Array of selected files
|
|
569
|
+
| onChange | `(files: FileWithPreview[]) => void` | Required | Callback when files change
|
|
570
|
+
| multiple | `boolean` | `false` | Allow multiple file selection
|
|
571
|
+
| accept | `Record<string, string[]>` | `{ "image/*": [".png", ".jpg", ".jpeg", ".gif"], "application/pdf": [".pdf"] }` | Accepted file types
|
|
572
|
+
| maxSize | `number` | 2MB | Maximum file size in bytes
|
|
573
|
+
| maxFiles | `number` | 5 | Maximum number of files
|
|
574
|
+
| disabled | `boolean` | `false` | Disable the input
|
|
575
|
+
| loading | `boolean` | `false` | Show loading state
|
|
576
|
+
| progress | `number | number[]` | - | Upload progress percentage
|
|
577
|
+
| onRemove | `(file: FileWithPreview) => void` | - | Callback when a file is removed
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
#### Example Usage
|
|
581
|
+
|
|
582
|
+
```tsx
|
|
583
|
+
import { FileInput } from '@smartflowssbu/component-pack';
|
|
584
|
+
import { useState } from 'react';
|
|
585
|
+
|
|
586
|
+
function FileInputExample() {
|
|
587
|
+
const [files, setFiles] = useState([]);
|
|
588
|
+
|
|
589
|
+
return (
|
|
590
|
+
<FileInput
|
|
591
|
+
value={files}
|
|
592
|
+
onChange={setFiles}
|
|
593
|
+
multiple={true}
|
|
594
|
+
maxSize={5 * 1024 * 1024} // 5MB
|
|
595
|
+
/>
|
|
596
|
+
);
|
|
597
|
+
}
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
### MultiSelect
|
|
601
|
+
|
|
602
|
+
A flexible select component that supports single or multiple selection with grouping, search, and responsive behavior.
|
|
603
|
+
|
|
604
|
+
#### Props
|
|
605
|
+
|
|
606
|
+
| Prop | Type | Default | Description
|
|
607
|
+
|-----|-----|-----|-----
|
|
608
|
+
| options | `OptionType[]` | Required | Array of selectable options with format `{label: string, value: string, group?: string}`
|
|
609
|
+
| selected | `string[]` | Required | Array of selected option values
|
|
610
|
+
| onChange | `(value: string[]) => void` | Required | Callback when selection changes
|
|
611
|
+
| placeholder | `string` | `"Select..."` | Placeholder text
|
|
612
|
+
| className | `string` | - | Additional CSS classes for the main component
|
|
613
|
+
| multiple | `boolean` | `false` | Allow multiple selection
|
|
614
|
+
| onLoadMore | `() => void` | - | Callback for infinite loading
|
|
615
|
+
| hasMore | `boolean` | `false` | Indicates if more options can be loaded
|
|
616
|
+
| isDialog | `boolean` | `false` | Use dialog instead of drawer on mobile
|
|
617
|
+
| triggerClassName | `string` | - | Custom class for the trigger button
|
|
618
|
+
| contentClassName | `string` | - | Custom class for the dropdown content
|
|
619
|
+
| badgeClassName | `string` | - | Custom class for the selected item badges
|
|
620
|
+
| commandClassName | `string` | - | Custom class for the command component
|
|
621
|
+
| commandInputClassName | `string` | - | Custom class for the search input
|
|
622
|
+
| commandListClassName | `string` | - | Custom class for the options list
|
|
623
|
+
| commandItemClassName | `string` | - | Custom class for individual option items
|
|
624
|
+
| commandEmptyClassName | `string` | - | Custom class for the empty state
|
|
625
|
+
| commandGroupClassName | `string` | - | Custom class for option groups
|
|
626
|
+
| commandSeparatorClassName | `string` | - | Custom class for group separators
|
|
627
|
+
| align | `"start" \| "center" \| "end"` | `"start"` | Alignment of the dropdown relative to the trigger
|
|
628
|
+
| sideOffset | `number` | `5` | Offset from the trigger
|
|
629
|
+
| width | `"auto" \| "trigger" \| string` | `"trigger"` | Width of the dropdown content
|
|
630
|
+
|
|
631
|
+
#### Example Usage
|
|
632
|
+
|
|
633
|
+
```tsx
|
|
634
|
+
import { MultiSelect } from '@smartflowssbu/component-pack';
|
|
635
|
+
import { useState } from 'react';
|
|
636
|
+
|
|
637
|
+
function MultiSelectExample() {
|
|
638
|
+
const [selected, setSelected] = useState(['react']);
|
|
639
|
+
|
|
640
|
+
const options = [
|
|
641
|
+
{ label: 'React', value: 'react', group: 'Frontend' },
|
|
642
|
+
{ label: 'Vue', value: 'vue', group: 'Frontend' },
|
|
643
|
+
{ label: 'Angular', value: 'angular', group: 'Frontend' },
|
|
644
|
+
{ label: 'Node.js', value: 'nodejs', group: 'Backend' },
|
|
645
|
+
{ label: 'Express', value: 'express', group: 'Backend' },
|
|
646
|
+
];
|
|
647
|
+
|
|
648
|
+
return (
|
|
649
|
+
<MultiSelect
|
|
650
|
+
options={options}
|
|
651
|
+
selected={selected}
|
|
652
|
+
onChange={setSelected}
|
|
653
|
+
placeholder="Select technologies..."
|
|
654
|
+
multiple={true}
|
|
655
|
+
className="w-[350px]"
|
|
656
|
+
badgeClassName="bg-blue-100 text-blue-800"
|
|
657
|
+
/>
|
|
658
|
+
);
|
|
659
|
+
}
|
|
660
|
+
````
|
|
661
|
+
|
|
662
|
+
### MultiStepper
|
|
663
|
+
|
|
664
|
+
A comprehensive multi-step form component with navigation, validation, and customizable steps.
|
|
665
|
+
|
|
666
|
+
#### Props
|
|
667
|
+
|
|
668
|
+
| Prop | Type | Default | Description
|
|
669
|
+
|-----|-----|-----|-----
|
|
670
|
+
| context | `React.Context<UseMultiStepFormTypeOptions<T>>` | Required | Context created with buildMultiStepForm
|
|
671
|
+
| previousLabel | `string` | `"Previous"` | Label for the previous button
|
|
672
|
+
| nextLabel | `string` | `"Next"` | Label for the next button
|
|
673
|
+
| endStepLabel | `string` | `"Submit"` | Label for the submit button
|
|
674
|
+
| showNavbar | `boolean` | `true` | Show the step navigation bar
|
|
675
|
+
| showButtons | `boolean` | `true` | Show the navigation buttons
|
|
676
|
+
| isLoading | `boolean` | `false` | Loading state for the submit button
|
|
677
|
+
| className | `string` | `""` | Additional CSS classes
|
|
678
|
+
| debug | `boolean` | `false` | Show debug information
|
|
679
|
+
|
|
680
|
+
#### Utility Functions
|
|
681
|
+
|
|
682
|
+
| Function | Description
|
|
683
|
+
|-----|-----
|
|
684
|
+
| buildMultiStepForm | Creates a context and provider for a multi-step form
|
|
685
|
+
| useMultiStepForm | Hook for accessing multi-step form functionality
|
|
686
|
+
|
|
687
|
+
#### Example Usage
|
|
688
|
+
|
|
689
|
+
```tsx
|
|
690
|
+
import {
|
|
691
|
+
buildMultiStepForm,
|
|
692
|
+
useMultiStepForm,
|
|
693
|
+
Form
|
|
694
|
+
} from '@smartflowssbu/component-pack';
|
|
695
|
+
import { z } from 'zod';
|
|
696
|
+
import { useForm, FormProvider, useFormContext } from 'react-hook-form';
|
|
697
|
+
import {
|
|
698
|
+
FormField,
|
|
699
|
+
FormItem,
|
|
700
|
+
FormLabel,
|
|
701
|
+
FormControl,
|
|
702
|
+
FormMessage
|
|
703
|
+
} from '@/components/ui/form';
|
|
704
|
+
import { Input } from '@/components/ui/input';
|
|
705
|
+
import { PasswordInput } from '@smartflowssbu/component-pack';
|
|
706
|
+
import { MultiSelect } from '@smartflowssbu/component-pack';
|
|
707
|
+
import { FileInput } from '@smartflowssbu/component-pack';
|
|
708
|
+
|
|
709
|
+
// Define your form schema
|
|
710
|
+
export const signupSchema = z.object({
|
|
711
|
+
name: z.string().min(2, "Name must be at least 2 characters"),
|
|
712
|
+
email: z.string().email("Please enter a valid email"),
|
|
713
|
+
phone: z.string().min(10, "Please enter a valid phone number"),
|
|
714
|
+
password: z.string().min(8, "Password must be at least 8 characters"),
|
|
715
|
+
password_confirmation: z.string(),
|
|
716
|
+
roles: z.array(z.string()),
|
|
717
|
+
permissions: z.array(z.string()),
|
|
718
|
+
image: z.any().optional(),
|
|
719
|
+
}).refine((data) => data.password === data.password_confirmation, {
|
|
720
|
+
message: "Passwords do not match",
|
|
721
|
+
path: ["password_confirmation"],
|
|
722
|
+
});
|
|
723
|
+
|
|
724
|
+
// Step 1: Basic Information
|
|
725
|
+
export function Step1() {
|
|
726
|
+
const { control } = useFormContext();
|
|
727
|
+
|
|
728
|
+
return (
|
|
729
|
+
<>
|
|
730
|
+
<FormField
|
|
731
|
+
control={control}
|
|
732
|
+
name="name"
|
|
733
|
+
render={({ field }) => (
|
|
734
|
+
<FormItem>
|
|
735
|
+
<FormLabel>Name</FormLabel>
|
|
736
|
+
<FormControl>
|
|
737
|
+
<Input placeholder="Enter your name" {...field} />
|
|
738
|
+
</FormControl>
|
|
739
|
+
<FormMessage />
|
|
740
|
+
</FormItem>
|
|
741
|
+
)}
|
|
742
|
+
/>
|
|
743
|
+
<FormField
|
|
744
|
+
control={control}
|
|
745
|
+
name="email"
|
|
746
|
+
render={({ field }) => (
|
|
747
|
+
<FormItem>
|
|
748
|
+
<FormLabel>Email</FormLabel>
|
|
749
|
+
<FormControl>
|
|
750
|
+
<Input placeholder="Enter your email" {...field} />
|
|
751
|
+
</FormControl>
|
|
752
|
+
<FormMessage />
|
|
753
|
+
</FormItem>
|
|
754
|
+
)}
|
|
755
|
+
/>
|
|
756
|
+
<FormField
|
|
757
|
+
control={control}
|
|
758
|
+
name="phone"
|
|
759
|
+
render={({ field }) => (
|
|
760
|
+
<FormItem>
|
|
761
|
+
<FormLabel>Phone</FormLabel>
|
|
762
|
+
<FormControl>
|
|
763
|
+
<Input placeholder="Enter your phone number" {...field} />
|
|
764
|
+
</FormControl>
|
|
765
|
+
<FormMessage />
|
|
766
|
+
</FormItem>
|
|
767
|
+
)}
|
|
768
|
+
/>
|
|
769
|
+
</>
|
|
770
|
+
);
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
// Step 2: Security
|
|
774
|
+
export function Step2() {
|
|
775
|
+
const { control } = useFormContext();
|
|
776
|
+
|
|
777
|
+
return (
|
|
778
|
+
<>
|
|
779
|
+
<FormField
|
|
780
|
+
control={control}
|
|
781
|
+
name="password"
|
|
782
|
+
render={({ field }) => (
|
|
783
|
+
<FormItem>
|
|
784
|
+
<FormLabel>Password</FormLabel>
|
|
785
|
+
<FormControl>
|
|
786
|
+
<PasswordInput placeholder="Enter your password" {...field} />
|
|
787
|
+
</FormControl>
|
|
788
|
+
<FormMessage />
|
|
789
|
+
</FormItem>
|
|
790
|
+
)}
|
|
791
|
+
/>
|
|
792
|
+
<FormField
|
|
793
|
+
control={control}
|
|
794
|
+
name="password_confirmation"
|
|
795
|
+
render={({ field }) => (
|
|
796
|
+
<FormItem>
|
|
797
|
+
<FormLabel>Confirm Password</FormLabel>
|
|
798
|
+
<FormControl>
|
|
799
|
+
<PasswordInput placeholder="Confirm your password" {...field} />
|
|
800
|
+
</FormControl>
|
|
801
|
+
<FormMessage />
|
|
802
|
+
</FormItem>
|
|
803
|
+
)}
|
|
804
|
+
/>
|
|
805
|
+
</>
|
|
806
|
+
);
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
// Step 3: Roles & Permissions
|
|
810
|
+
export function Step3() {
|
|
811
|
+
const { control } = useFormContext();
|
|
812
|
+
|
|
813
|
+
const roleOptions = [
|
|
814
|
+
{ label: 'Admin', value: 'admin' },
|
|
815
|
+
{ label: 'User', value: 'user' },
|
|
816
|
+
{ label: 'Editor', value: 'editor' },
|
|
817
|
+
];
|
|
818
|
+
|
|
819
|
+
const permissionOptions = [
|
|
820
|
+
{ label: 'Create', value: 'create', group: 'Content' },
|
|
821
|
+
{ label: 'Edit', value: 'edit', group: 'Content' },
|
|
822
|
+
{ label: 'Delete', value: 'delete', group: 'Content' },
|
|
823
|
+
{ label: 'View Users', value: 'view_users', group: 'Users' },
|
|
824
|
+
{ label: 'Manage Users', value: 'manage_users', group: 'Users' },
|
|
825
|
+
];
|
|
826
|
+
|
|
827
|
+
return (
|
|
828
|
+
<>
|
|
829
|
+
<FormField
|
|
830
|
+
control={control}
|
|
831
|
+
name="roles"
|
|
832
|
+
render={({ field }) => (
|
|
833
|
+
<FormItem>
|
|
834
|
+
<FormLabel>Roles</FormLabel>
|
|
835
|
+
<FormControl>
|
|
836
|
+
<MultiSelect
|
|
837
|
+
options={roleOptions}
|
|
838
|
+
selected={field.value || []}
|
|
839
|
+
onChange={field.onChange}
|
|
840
|
+
placeholder="Select roles"
|
|
841
|
+
multiple={true}
|
|
842
|
+
/>
|
|
843
|
+
</FormControl>
|
|
844
|
+
<FormMessage />
|
|
845
|
+
</FormItem>
|
|
846
|
+
)}
|
|
847
|
+
/>
|
|
848
|
+
<FormField
|
|
849
|
+
control={control}
|
|
850
|
+
name="permissions"
|
|
851
|
+
render={({ field }) => (
|
|
852
|
+
<FormItem>
|
|
853
|
+
<FormLabel>Permissions</FormLabel>
|
|
854
|
+
<FormControl>
|
|
855
|
+
<MultiSelect
|
|
856
|
+
options={permissionOptions}
|
|
857
|
+
selected={field.value || []}
|
|
858
|
+
onChange={field.onChange}
|
|
859
|
+
placeholder="Select permissions"
|
|
860
|
+
multiple={true}
|
|
861
|
+
/>
|
|
862
|
+
</FormControl>
|
|
863
|
+
<FormMessage />
|
|
864
|
+
</FormItem>
|
|
865
|
+
)}
|
|
866
|
+
/>
|
|
867
|
+
</>
|
|
868
|
+
);
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
// Step 4: Profile Picture
|
|
872
|
+
export function Step4() {
|
|
873
|
+
const { control } = useFormContext();
|
|
874
|
+
|
|
875
|
+
return (
|
|
876
|
+
<FormField
|
|
877
|
+
control={control}
|
|
878
|
+
name="image"
|
|
879
|
+
render={({ field }) => (
|
|
880
|
+
<FormItem>
|
|
881
|
+
<FormLabel>Profile Picture</FormLabel>
|
|
882
|
+
<FormControl>
|
|
883
|
+
<FileInput
|
|
884
|
+
value={field.value || []}
|
|
885
|
+
onChange={field.onChange}
|
|
886
|
+
accept={{ "image/*": [".png", ".jpg", ".jpeg"] }}
|
|
887
|
+
maxSize={2 * 1024 * 1024} // 2MB
|
|
888
|
+
/>
|
|
889
|
+
</FormControl>
|
|
890
|
+
<FormMessage />
|
|
891
|
+
</FormItem>
|
|
892
|
+
)}
|
|
893
|
+
/>
|
|
894
|
+
);
|
|
895
|
+
}
|
|
896
|
+
```
|
|
897
|
+
|
|
898
|
+
```tsx
|
|
899
|
+
//Sign Up Step Config
|
|
900
|
+
import React from 'react'
|
|
901
|
+
import {
|
|
902
|
+
Card,
|
|
903
|
+
CardContent,
|
|
904
|
+
CardDescription,
|
|
905
|
+
CardFooter,
|
|
906
|
+
CardHeader,
|
|
907
|
+
CardTitle
|
|
908
|
+
} from '@/components/ui/card'
|
|
909
|
+
import { Social } from '@/components/auth/social'
|
|
910
|
+
import { BackButton } from '@/components/auth/back-button'
|
|
911
|
+
import { cn } from "@/lib/utils"
|
|
912
|
+
|
|
913
|
+
interface AuthWrapperProps {
|
|
914
|
+
children: React.ReactNode
|
|
915
|
+
headerTitle: string
|
|
916
|
+
headerDescription: string
|
|
917
|
+
backButtonLabel: string
|
|
918
|
+
backLinkLabel: string
|
|
919
|
+
backButtonHref: string
|
|
920
|
+
enableSocial: boolean
|
|
921
|
+
onSocialLogin: (provider: string) => void
|
|
922
|
+
showGoogle?: boolean
|
|
923
|
+
showFacebook?: boolean
|
|
924
|
+
showApple?: boolean
|
|
925
|
+
showGithub?: boolean
|
|
926
|
+
showSlack?: boolean
|
|
927
|
+
showX?: boolean
|
|
928
|
+
showLinkedin?: boolean
|
|
929
|
+
showMicrosoft?: boolean
|
|
930
|
+
className?: string
|
|
931
|
+
contentClassName?: string
|
|
932
|
+
width?: string;
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
export function AuthWrapper({
|
|
936
|
+
children,
|
|
937
|
+
headerTitle,
|
|
938
|
+
headerDescription,
|
|
939
|
+
backButtonLabel,
|
|
940
|
+
backLinkLabel,
|
|
941
|
+
backButtonHref,
|
|
942
|
+
enableSocial,
|
|
943
|
+
onSocialLogin,
|
|
944
|
+
className,
|
|
945
|
+
contentClassName,
|
|
946
|
+
width = "w-[350px]",
|
|
947
|
+
...socialProps
|
|
948
|
+
}: AuthWrapperProps) {
|
|
949
|
+
return (
|
|
950
|
+
<Card className={cn(width, className)}>
|
|
951
|
+
<CardHeader className="space-y-1">
|
|
952
|
+
<CardTitle>{headerTitle}</CardTitle>
|
|
953
|
+
<CardDescription>{headerDescription}</CardDescription>
|
|
954
|
+
</CardHeader>
|
|
955
|
+
<CardContent className={cn(contentClassName)}>{children}</CardContent>
|
|
956
|
+
<CardFooter className="flex flex-col items-center gap-4">
|
|
957
|
+
{enableSocial && (
|
|
958
|
+
<Social onSocialLogin={onSocialLogin} {...socialProps} />
|
|
959
|
+
)}
|
|
960
|
+
<BackButton
|
|
961
|
+
backLinkLabel={backLinkLabel}
|
|
962
|
+
label={backButtonLabel}
|
|
963
|
+
href={backButtonHref}
|
|
964
|
+
/>
|
|
965
|
+
</CardFooter>
|
|
966
|
+
</Card>
|
|
967
|
+
)
|
|
968
|
+
}
|
|
969
|
+
```
|
|
970
|
+
|
|
971
|
+
```tsx
|
|
972
|
+
// Use the Sign Up Dorm
|
|
973
|
+
"use client";
|
|
974
|
+
import {containerSignUpForm as container} from "@/constants/framer-motion";
|
|
975
|
+
import {useMultiStepForm, MultiStepNavbar, MultiStepNavButtons} from "@smartflowssbu/component-pack";
|
|
976
|
+
import {motion} from "framer-motion";
|
|
977
|
+
import MultiStepForm from "@/app/(auth)/signup/_components/multi-step-form";
|
|
978
|
+
import {SignUpFormContext} from "@/app/(auth)/signup/_components/signup-step-config";
|
|
979
|
+
|
|
980
|
+
export default function SignUpForm() {
|
|
981
|
+
const {CurrentForm} = useMultiStepForm(SignUpFormContext);
|
|
982
|
+
|
|
983
|
+
return (
|
|
984
|
+
<MultiStepForm title="Sign up" description="Create an account to get started">
|
|
985
|
+
<MultiStepNavbar context={SignUpFormContext}/>
|
|
986
|
+
<div className="flex-1">
|
|
987
|
+
<motion.div variants={container} className="space-y-6" initial="hidden" animate="visible" exit="exit">
|
|
988
|
+
<CurrentForm/>
|
|
989
|
+
</motion.div>
|
|
990
|
+
<MultiStepNavButtons
|
|
991
|
+
context={SignUpFormContext}
|
|
992
|
+
previousLabel="Previous"
|
|
993
|
+
nextLabel="Next"
|
|
994
|
+
endStepLabel="Submit"
|
|
995
|
+
/>
|
|
996
|
+
</div>
|
|
997
|
+
</MultiStepForm>
|
|
998
|
+
);
|
|
999
|
+
};
|
|
1000
|
+
```
|
|
1001
|
+
|
|
1002
|
+
```tsx
|
|
1003
|
+
//Use the Multi Step Form
|
|
1004
|
+
"use client";
|
|
1005
|
+
import {containerMultiStepForm as container} from "@/constants/framer-motion";
|
|
1006
|
+
import {useMultiStepForm} from "@/hooks/multi-step-form";
|
|
1007
|
+
import {motion} from "framer-motion";
|
|
1008
|
+
import {Form} from "@/components/ui/form";
|
|
1009
|
+
import React, {PropsWithChildren} from "react";
|
|
1010
|
+
import {SignUpFormContext} from "@/app/(auth)/signup/_components/signup-step-config";
|
|
1011
|
+
import {AuthWrapper} from "@/components/auth/auth-wrapper";
|
|
1012
|
+
|
|
1013
|
+
interface Props extends PropsWithChildren {
|
|
1014
|
+
title: string;
|
|
1015
|
+
description: string;
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
const MultiStepForm = ({title, description, children}: Props) => {
|
|
1019
|
+
const {form, onSubmit, onErrors} = useMultiStepForm(SignUpFormContext);
|
|
1020
|
+
|
|
1021
|
+
function handleSocialLogin(provider: string) {
|
|
1022
|
+
// Implement social signin logic here
|
|
1023
|
+
console.log(Social login with ${provider})
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
return (
|
|
1027
|
+
<Form {...form}>
|
|
1028
|
+
<form onSubmit={form?.handleSubmit(onSubmit, onErrors)}>
|
|
1029
|
+
<motion.div
|
|
1030
|
+
variants={container}
|
|
1031
|
+
className="flex flex-col gap-2"
|
|
1032
|
+
initial="hidden"
|
|
1033
|
+
animate="visible"
|
|
1034
|
+
exit="exit"
|
|
1035
|
+
>
|
|
1036
|
+
<AuthWrapper
|
|
1037
|
+
headerTitle={title}
|
|
1038
|
+
headerDescription={description}
|
|
1039
|
+
backButtonLabel='Already have an account?'
|
|
1040
|
+
backLinkLabel='Sign in'
|
|
1041
|
+
backButtonHref='/signin'
|
|
1042
|
+
enableSocial={false}
|
|
1043
|
+
onSocialLogin={handleSocialLogin}
|
|
1044
|
+
width='w-[500px]'
|
|
1045
|
+
contentClassName='flex flex-col sm:flex-row gap-2 p-2 m-2'>
|
|
1046
|
+
{children}
|
|
1047
|
+
</AuthWrapper>
|
|
1048
|
+
</motion.div>
|
|
1049
|
+
</form>
|
|
1050
|
+
</Form>
|
|
1051
|
+
);
|
|
1052
|
+
};
|
|
1053
|
+
|
|
1054
|
+
export default MultiStepForm;
|
|
1055
|
+
```
|
|
1056
|
+
|
|
1057
|
+
```tsx
|
|
1058
|
+
//Use Auth Wrapper
|
|
1059
|
+
import React from 'react'
|
|
1060
|
+
import {
|
|
1061
|
+
Card,
|
|
1062
|
+
CardContent,
|
|
1063
|
+
CardDescription,
|
|
1064
|
+
CardFooter,
|
|
1065
|
+
CardHeader,
|
|
1066
|
+
CardTitle
|
|
1067
|
+
} from '@/components/ui/card'
|
|
1068
|
+
import { Social } from '@/components/auth/social'
|
|
1069
|
+
import { BackButton } from '@/components/auth/back-button'
|
|
1070
|
+
import { cn } from "@/lib/utils"
|
|
1071
|
+
|
|
1072
|
+
interface AuthWrapperProps {
|
|
1073
|
+
children: React.ReactNode
|
|
1074
|
+
headerTitle: string
|
|
1075
|
+
headerDescription: string
|
|
1076
|
+
backButtonLabel: string
|
|
1077
|
+
backLinkLabel: string
|
|
1078
|
+
backButtonHref: string
|
|
1079
|
+
enableSocial: boolean
|
|
1080
|
+
onSocialLogin: (provider: string) => void
|
|
1081
|
+
showGoogle?: boolean
|
|
1082
|
+
showFacebook?: boolean
|
|
1083
|
+
showApple?: boolean
|
|
1084
|
+
showGithub?: boolean
|
|
1085
|
+
showSlack?: boolean
|
|
1086
|
+
showX?: boolean
|
|
1087
|
+
showLinkedin?: boolean
|
|
1088
|
+
showMicrosoft?: boolean
|
|
1089
|
+
className?: string
|
|
1090
|
+
contentClassName?: string
|
|
1091
|
+
width?: string;
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
export function AuthWrapper({
|
|
1095
|
+
children,
|
|
1096
|
+
headerTitle,
|
|
1097
|
+
headerDescription,
|
|
1098
|
+
backButtonLabel,
|
|
1099
|
+
backLinkLabel,
|
|
1100
|
+
backButtonHref,
|
|
1101
|
+
enableSocial,
|
|
1102
|
+
onSocialLogin,
|
|
1103
|
+
className,
|
|
1104
|
+
contentClassName,
|
|
1105
|
+
width = "w-[350px]",
|
|
1106
|
+
...socialProps
|
|
1107
|
+
}: AuthWrapperProps) {
|
|
1108
|
+
return (
|
|
1109
|
+
<Card className={cn(width, className)}>
|
|
1110
|
+
<CardHeader className="space-y-1">
|
|
1111
|
+
<CardTitle>{headerTitle}</CardTitle>
|
|
1112
|
+
<CardDescription>{headerDescription}</CardDescription>
|
|
1113
|
+
</CardHeader>
|
|
1114
|
+
<CardContent className={cn(contentClassName)}>{children}</CardContent>
|
|
1115
|
+
<CardFooter className="flex flex-col items-center gap-4">
|
|
1116
|
+
{enableSocial && (
|
|
1117
|
+
<Social onSocialLogin={onSocialLogin} {...socialProps} />
|
|
1118
|
+
)}
|
|
1119
|
+
<BackButton
|
|
1120
|
+
backLinkLabel={backLinkLabel}
|
|
1121
|
+
label={backButtonLabel}
|
|
1122
|
+
href={backButtonHref}
|
|
1123
|
+
/>
|
|
1124
|
+
</CardFooter>
|
|
1125
|
+
</Card>
|
|
1126
|
+
)
|
|
1127
|
+
}
|
|
1128
|
+
```
|
|
1129
|
+
|
|
1130
|
+
```tsx
|
|
1131
|
+
//Multi Ster SignUp Form
|
|
1132
|
+
"use client";
|
|
1133
|
+
|
|
1134
|
+
import {SignUpProvider} from "@/app/(auth)/signup/_components/signup-step-config";
|
|
1135
|
+
import SignUpForm from "@/app/(auth)/signup/_components/form/signup-form";
|
|
1136
|
+
|
|
1137
|
+
export default function MultiStepSignUp () {
|
|
1138
|
+
return (
|
|
1139
|
+
<SignUpProvider>
|
|
1140
|
+
<SignUpForm />
|
|
1141
|
+
</SignUpProvider>
|
|
1142
|
+
);
|
|
1143
|
+
};
|
|
1144
|
+
```
|
|
1145
|
+
|
|
1146
|
+
|
|
1147
|
+
```tsx
|
|
1148
|
+
//back button
|
|
1149
|
+
import Link from 'next/link'
|
|
1150
|
+
|
|
1151
|
+
interface BackButtonProps {
|
|
1152
|
+
label: string
|
|
1153
|
+
href: string
|
|
1154
|
+
backLinkLabel: string
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
export function BackButton({ backLinkLabel, label, href }: BackButtonProps) {
|
|
1158
|
+
return (
|
|
1159
|
+
<div className="text-sm text-muted-foreground text-center w-full">
|
|
1160
|
+
{label}{' '}
|
|
1161
|
+
<Link href={href} className="text-primary underline-offset-4 hover:underline">
|
|
1162
|
+
{backLinkLabel}
|
|
1163
|
+
</Link>
|
|
1164
|
+
</div>
|
|
1165
|
+
)
|
|
1166
|
+
}
|
|
1167
|
+
```
|
|
1168
|
+
|
|
1169
|
+
### PasswordInput
|
|
1170
|
+
|
|
1171
|
+
A password input component with show/hide functionality.
|
|
1172
|
+
|
|
1173
|
+
#### Props
|
|
1174
|
+
|
|
1175
|
+
| Prop | Type | Default | Description
|
|
1176
|
+
|-----|-----|-----|-----
|
|
1177
|
+
| className | `string` | - | Additional CSS classes
|
|
1178
|
+
| showPasswordLabel | `string` | `"Show password"` | Screen reader label for show password button
|
|
1179
|
+
| hidePasswordLabel | `string` | `"Hide password"` | Screen reader label for hide password button
|
|
1180
|
+
| ...props | `React.InputHTMLAttributes<HTMLInputElement>` | - | All standard input props
|
|
1181
|
+
|
|
1182
|
+
|
|
1183
|
+
#### Example Usage
|
|
1184
|
+
|
|
1185
|
+
```tsx
|
|
1186
|
+
import { PasswordInput } from '@smartflowssbu/component-pack';
|
|
1187
|
+
import { useState } from 'react';
|
|
1188
|
+
|
|
1189
|
+
function PasswordInputExample() {
|
|
1190
|
+
const [password, setPassword] = useState('');
|
|
1191
|
+
|
|
1192
|
+
return (
|
|
1193
|
+
<PasswordInput
|
|
1194
|
+
value={password}
|
|
1195
|
+
onChange={(e) => setPassword(e.target.value)}
|
|
1196
|
+
placeholder="Enter your password"
|
|
1197
|
+
/>
|
|
1198
|
+
);
|
|
1199
|
+
}
|
|
1200
|
+
```
|
|
1201
|
+
|
|
1202
|
+
### PhoneInput
|
|
1203
|
+
|
|
1204
|
+
An international phone number input with country selection and validation.
|
|
1205
|
+
|
|
1206
|
+
#### Props
|
|
1207
|
+
|
|
1208
|
+
| Prop | Type | Default | Description
|
|
1209
|
+
|-----|-----|-----|-----
|
|
1210
|
+
| value | `string` | - | Phone number value
|
|
1211
|
+
| defaultCountry | `CountryCode` | `"US"` | Default country code
|
|
1212
|
+
| ...props | `React.ComponentPropsWithoutRef<'input'>` | - | All standard input props
|
|
1213
|
+
|
|
1214
|
+
|
|
1215
|
+
#### Example Usage
|
|
1216
|
+
|
|
1217
|
+
```tsx
|
|
1218
|
+
import { PhoneInput, getPhoneData } from '@smartflowssbu/component-pack';
|
|
1219
|
+
import { useState, useEffect } from 'react';
|
|
1220
|
+
|
|
1221
|
+
function PhoneInputExample() {
|
|
1222
|
+
const [phone, setPhone] = useState('+1');
|
|
1223
|
+
const [phoneData, setPhoneData] = useState(getPhoneData(phone));
|
|
1224
|
+
|
|
1225
|
+
useEffect(() => {
|
|
1226
|
+
setPhoneData(getPhoneData(phone));
|
|
1227
|
+
}, [phone]);
|
|
1228
|
+
|
|
1229
|
+
return (
|
|
1230
|
+
<PhoneInput
|
|
1231
|
+
value={phone}
|
|
1232
|
+
onChange={(e) => setPhone(e.target.value)}
|
|
1233
|
+
defaultCountry="US"
|
|
1234
|
+
/>
|
|
1235
|
+
);
|
|
1236
|
+
}
|
|
1237
|
+
```
|
|
1238
|
+
|
|
1239
|
+
### ResponsiveAlertDialog
|
|
1240
|
+
|
|
1241
|
+
An alert dialog component that adapts to desktop (modal) and mobile (drawer) views.
|
|
1242
|
+
|
|
1243
|
+
#### Props
|
|
1244
|
+
|
|
1245
|
+
| Prop | Type | Default | Description
|
|
1246
|
+
|-----|-----|-----|-----
|
|
1247
|
+
| trigger | `React.ReactNode` | Required | Element that triggers the dialog
|
|
1248
|
+
| title | `string` | Required | Dialog title
|
|
1249
|
+
| description | `string` | Required | Dialog description
|
|
1250
|
+
| cancelText | `string` | `"Cancel"` | Text for the cancel button
|
|
1251
|
+
| confirmText | `string` | `"Continue"` | Text for the confirm button
|
|
1252
|
+
| onConfirm | `() => void` | Required | Callback when confirm button is clicked
|
|
1253
|
+
|
|
1254
|
+
|
|
1255
|
+
#### Example Usage
|
|
1256
|
+
|
|
1257
|
+
```tsx
|
|
1258
|
+
import { ResponsiveAlertDialog } from '@smartflowssbu/component-pack';
|
|
1259
|
+
import { Button } from '@smartflowssbu/component-pack';
|
|
1260
|
+
|
|
1261
|
+
function AlertDialogExample() {
|
|
1262
|
+
const handleDelete = () => {
|
|
1263
|
+
console.log('Item deleted');
|
|
1264
|
+
};
|
|
1265
|
+
|
|
1266
|
+
return (
|
|
1267
|
+
<ResponsiveAlertDialog
|
|
1268
|
+
trigger={<Button variant="destructive">Delete Item</Button>}
|
|
1269
|
+
title="Are you sure?"
|
|
1270
|
+
description="This action cannot be undone. This will permanently delete the item."
|
|
1271
|
+
cancelText="Cancel"
|
|
1272
|
+
confirmText="Delete"
|
|
1273
|
+
onConfirm={handleDelete}
|
|
1274
|
+
/>
|
|
1275
|
+
);
|
|
1276
|
+
}
|
|
1277
|
+
```
|
|
1278
|
+
|
|
1279
|
+
### ResponsiveDialog
|
|
1280
|
+
|
|
1281
|
+
A dialog component that adapts to desktop (modal) and mobile (drawer) views.
|
|
1282
|
+
|
|
1283
|
+
#### Props
|
|
1284
|
+
|
|
1285
|
+
| Prop | Type | Default | Description
|
|
1286
|
+
|-----|-----|-----|-----
|
|
1287
|
+
| trigger | `React.ReactNode` | Required | Element that triggers the dialog
|
|
1288
|
+
| title | `string` | Required | Dialog title
|
|
1289
|
+
| description | `string` | Required | Dialog description
|
|
1290
|
+
| children | `React.ReactNode` | Required | Dialog content
|
|
1291
|
+
|
|
1292
|
+
|
|
1293
|
+
#### Example Usage
|
|
1294
|
+
|
|
1295
|
+
```tsx
|
|
1296
|
+
import { ResponsiveDialog } from '@smartflowssbu/component-pack';
|
|
1297
|
+
import { Button } from '@smartflowssbu/component-pack';
|
|
1298
|
+
|
|
1299
|
+
function DialogExample() {
|
|
1300
|
+
return (
|
|
1301
|
+
<ResponsiveDialog
|
|
1302
|
+
trigger={<Button>Open Dialog</Button>}
|
|
1303
|
+
title="Edit Profile"
|
|
1304
|
+
description="Make changes to your profile here."
|
|
1305
|
+
>
|
|
1306
|
+
<form className="space-y-4 pt-4">
|
|
1307
|
+
<div className="space-y-2">
|
|
1308
|
+
<label htmlFor="name">Name</label>
|
|
1309
|
+
<input id="name" className="w-full p-2 border rounded" />
|
|
1310
|
+
</div>
|
|
1311
|
+
<div className="space-y-2">
|
|
1312
|
+
<label htmlFor="email">Email</label>
|
|
1313
|
+
<input id="email" type="email" className="w-full p-2 border rounded" />
|
|
1314
|
+
</div>
|
|
1315
|
+
<div className="flex justify-end space-x-2">
|
|
1316
|
+
<Button type="button" variant="outline">Cancel</Button>
|
|
1317
|
+
<Button type="submit">Save</Button>
|
|
1318
|
+
</div>
|
|
1319
|
+
</form>
|
|
1320
|
+
</ResponsiveDialog>
|
|
1321
|
+
);
|
|
1322
|
+
}
|
|
1323
|
+
```
|
|
1324
|
+
|
|
1325
|
+
### TagInput
|
|
1326
|
+
|
|
1327
|
+
A component for adding and managing tags with suggestions and validation.
|
|
1328
|
+
|
|
1329
|
+
#### Props
|
|
1330
|
+
|
|
1331
|
+
| Prop | Type | Default | Description
|
|
1332
|
+
|-----|-----|-----|-----
|
|
1333
|
+
| placeholder | `string` | `"Add tag..."` | Placeholder text
|
|
1334
|
+
| tags | `string[]` | Required | Array of current tags
|
|
1335
|
+
| setTags | `(tags: string[]) => void` | Required | Callback when tags change
|
|
1336
|
+
| suggestions | `string[]` | `[]` | Array of tag suggestions
|
|
1337
|
+
| maxTags | `number` | - | Maximum number of tags allowed
|
|
1338
|
+
| disabled | `boolean` | `false` | Disable the input
|
|
1339
|
+
| error | `string` | - | Error message to display
|
|
1340
|
+
|
|
1341
|
+
|
|
1342
|
+
#### Example Usage
|
|
1343
|
+
|
|
1344
|
+
```tsx
|
|
1345
|
+
import { TagInput } from '@smartflowssbu/component-pack';
|
|
1346
|
+
import { useState } from 'react';
|
|
1347
|
+
|
|
1348
|
+
function TagInputExample() {
|
|
1349
|
+
const [tags, setTags] = useState(['react', 'typescript']);
|
|
1350
|
+
|
|
1351
|
+
const suggestions = [
|
|
1352
|
+
'react', 'vue', 'angular', 'svelte', 'javascript',
|
|
1353
|
+
'typescript', 'node', 'express', 'mongodb'
|
|
1354
|
+
];
|
|
1355
|
+
|
|
1356
|
+
return (
|
|
1357
|
+
<TagInput
|
|
1358
|
+
tags={tags}
|
|
1359
|
+
setTags={setTags}
|
|
1360
|
+
suggestions={suggestions}
|
|
1361
|
+
placeholder="Add technologies..."
|
|
1362
|
+
/>
|
|
1363
|
+
);
|
|
1364
|
+
}
|
|
1365
|
+
```
|
|
1366
|
+
|
|
1367
|
+
## Styling
|
|
1368
|
+
|
|
1369
|
+
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.
|
|
1370
|
+
|
|
1371
|
+
### Custom Styling Example
|
|
1372
|
+
|
|
1373
|
+
```tsx
|
|
1374
|
+
<Breadcrumbs
|
|
1375
|
+
className="text-blue-600 dark:text-blue-400"
|
|
1376
|
+
segments={[
|
|
1377
|
+
{ label: 'Home', href: '/' },
|
|
1378
|
+
{ label: 'Products', href: '/products' },
|
|
1379
|
+
{ label: 'Current Page' },
|
|
1380
|
+
]}
|
|
1381
|
+
/>
|
|
1382
|
+
|
|
1383
|
+
<ColorPicker
|
|
1384
|
+
className="w-[300px] rounded-lg"
|
|
1385
|
+
color="#3B82F6"
|
|
1386
|
+
onChange={setColor}
|
|
1387
|
+
/>
|
|
1388
|
+
```
|
|
1389
|
+
|
|
1390
|
+
## TypeScript Support
|
|
1391
|
+
|
|
1392
|
+
All components include full TypeScript support out of the box. Type definitions are automatically available when you import components.
|
|
1393
|
+
|
|
1394
|
+
## Contributing
|
|
1395
|
+
|
|
1396
|
+
Please read our [Contributing Guide](CONTRIBUTING.md) before submitting a Pull Request.
|
|
1397
|
+
|
|
1398
|
+
## License
|
|
1399
|
+
|
|
1400
|
+
MIT © [Smartflowtech Team](https://smartflowtech.com)
|