@dsbtek/component-library 1.0.3 → 1.2.0

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # @dsbtek/component-library
2
2
 
3
- A collection of advanced React components built with shadcn/ui.
3
+ A collection of advanced React components built with TypeScript, Tailwind CSS, and shadcn/ui.
4
4
 
5
5
  ## Table of Contents
6
6
 
@@ -12,98 +12,100 @@ A collection of advanced React components built with shadcn/ui.
12
12
  - [DateTimePicker](#datetimepicker)
13
13
  - [FileInput](#fileinput)
14
14
  - [MultiSelect](#multiselect)
15
+ - [MultiStepper](#multistepper)
15
16
  - [PasswordInput](#passwordinput)
16
17
  - [PhoneInput](#phoneinput)
17
18
  - [ResponsiveAlertDialog](#responsivealertdialog)
18
19
  - [ResponsiveDialog](#responsivedialog)
20
+ - [TagInput](#taginput)
19
21
  - [Styling](#styling)
20
22
  - [Contributing](#contributing)
21
23
  - [License](#license)
22
24
 
23
25
  ## Installation
24
26
 
25
- 1. Install the package and its peer dependencies:
27
+ 1. Install the package:
26
28
 
27
29
  ```bash
28
- # Install the component library
29
30
  npm install @dsbtek/component-library
31
+ ````
30
32
 
31
- # Install peer dependencies
32
- npm install @radix-ui/react-popover cmdk class-variance-authority clsx lucide-react tailwind-merge
33
- ```
33
+ 2. Install peer dependencies:
34
34
 
35
- 2. Set up Tailwind CSS in your project:
36
35
 
37
36
  ```bash
38
- # Install Tailwind CSS and its dependencies
39
- npm install -D tailwindcss postcss autoprefixer tailwindcss-animate
37
+ npm install react@^18 react-dom@^18
40
38
  ```
41
39
 
42
- 3. Initialize Tailwind CSS if you haven't already:
40
+ 3. Set up Tailwind CSS:
41
+
43
42
 
44
43
  ```bash
44
+ npm install -D tailwindcss postcss autoprefixer
45
45
  npx tailwindcss init -p
46
46
  ```
47
47
 
48
48
  4. Update your `tailwind.config.js`:
49
49
 
50
+
50
51
  ```javascript
51
52
  /** @type {import('tailwindcss').Config} */
52
53
  module.exports = {
53
- darkMode: ['class'],
54
- content: [
55
- './src/**/*.{js,ts,jsx,tsx}',
56
- './node_modules/@dsbtek/component-library/**/*.{js,ts,jsx,tsx}',
57
- ],
58
- theme: {
59
- extend: {
60
- colors: {
61
- border: 'hsl(var(--border))',
62
- input: 'hsl(var(--input))',
63
- ring: 'hsl(var(--ring))',
64
- background: 'hsl(var(--background))',
65
- foreground: 'hsl(var(--foreground))',
66
- primary: {
67
- DEFAULT: 'hsl(var(--primary))',
68
- foreground: 'hsl(var(--primary-foreground))',
69
- },
70
- secondary: {
71
- DEFAULT: 'hsl(var(--secondary))',
72
- foreground: 'hsl(var(--secondary-foreground))',
73
- },
74
- destructive: {
75
- DEFAULT: 'hsl(var(--destructive))',
76
- foreground: 'hsl(var(--destructive-foreground))',
77
- },
78
- muted: {
79
- DEFAULT: 'hsl(var(--muted))',
80
- foreground: 'hsl(var(--muted-foreground))',
81
- },
82
- accent: {
83
- DEFAULT: 'hsl(var(--accent))',
84
- foreground: 'hsl(var(--accent-foreground))',
85
- },
86
- popover: {
87
- DEFAULT: 'hsl(var(--popover))',
88
- foreground: 'hsl(var(--popover-foreground))',
89
- },
90
- card: {
91
- DEFAULT: 'hsl(var(--card))',
92
- foreground: 'hsl(var(--card-foreground))',
93
- },
94
- },
95
- borderRadius: {
96
- lg: `var(--radius)`,
97
- md: `calc(var(--radius) - 2px)`,
98
- sm: 'calc(var(--radius) - 4px)',
99
- },
54
+ darkMode: ['class'],
55
+ content: [
56
+ './src/**/*.{js,ts,jsx,tsx}',
57
+ './node_modules/@dsbtek/component-library/**/*.{js,ts,jsx,tsx}',
58
+ ],
59
+ theme: {
60
+ extend: {
61
+ colors: {
62
+ border: 'hsl(var(--border))',
63
+ input: 'hsl(var(--input))',
64
+ ring: 'hsl(var(--ring))',
65
+ background: 'hsl(var(--background))',
66
+ foreground: 'hsl(var(--foreground))',
67
+ primary: {
68
+ DEFAULT: 'hsl(var(--primary))',
69
+ foreground: 'hsl(var(--primary-foreground))',
70
+ },
71
+ secondary: {
72
+ DEFAULT: 'hsl(var(--secondary))',
73
+ foreground: 'hsl(var(--secondary-foreground))',
74
+ },
75
+ destructive: {
76
+ DEFAULT: 'hsl(var(--destructive))',
77
+ foreground: 'hsl(var(--destructive-foreground))',
78
+ },
79
+ muted: {
80
+ DEFAULT: 'hsl(var(--muted))',
81
+ foreground: 'hsl(var(--muted-foreground))',
82
+ },
83
+ accent: {
84
+ DEFAULT: 'hsl(var(--accent))',
85
+ foreground: 'hsl(var(--accent-foreground))',
86
+ },
87
+ popover: {
88
+ DEFAULT: 'hsl(var(--popover))',
89
+ foreground: 'hsl(var(--popover-foreground))',
90
+ },
91
+ card: {
92
+ DEFAULT: 'hsl(var(--card))',
93
+ foreground: 'hsl(var(--card-foreground))',
100
94
  },
95
+ },
96
+ borderRadius: {
97
+ lg: 'var(--radius)',
98
+ md: 'calc(var(--radius) - 2px)',
99
+ sm: 'calc(var(--radius) - 4px)',
100
+ },
101
101
  },
102
- plugins: [require('tailwindcss-animate')],
102
+ },
103
+ plugins: [require('tailwindcss-animate')],
103
104
  };
104
105
  ```
105
106
 
106
- 5. Add the following CSS variables to your global CSS file:
107
+ 5. Add these CSS variables to your global CSS:
108
+
107
109
 
108
110
  ```css
109
111
  @tailwind base;
@@ -111,210 +113,807 @@ module.exports = {
111
113
  @tailwind utilities;
112
114
 
113
115
  @layer base {
114
- :root {
115
- --background: 0 0% 100%;
116
- --foreground: 222.2 84% 4.9%;
117
- --card: 0 0% 100%;
118
- --card-foreground: 222.2 84% 4.9%;
119
- --popover: 0 0% 100%;
120
- --popover-foreground: 222.2 84% 4.9%;
121
- --primary: 222.2 47.4% 11.2%;
122
- --primary-foreground: 210 40% 98%;
123
- --secondary: 210 40% 96.1%;
124
- --secondary-foreground: 222.2 47.4% 11.2%;
125
- --muted: 210 40% 96.1%;
126
- --muted-foreground: 215.4 16.3% 46.9%;
127
- --accent: 210 40% 96.1%;
128
- --accent-foreground: 222.2 47.4% 11.2%;
129
- --destructive: 0 84.2% 60.2%;
130
- --destructive-foreground: 210 40% 98%;
131
- --border: 214.3 31.8% 91.4%;
132
- --input: 214.3 31.8% 91.4%;
133
- --ring: 222.2 84% 4.9%;
134
- --radius: 0.5rem;
135
- }
116
+ :root {
117
+ --background: 0 0% 100%;
118
+ --foreground: 222.2 84% 4.9%;
119
+ --card: 0 0% 100%;
120
+ --card-foreground: 222.2 84% 4.9%;
121
+ --popover: 0 0% 100%;
122
+ --popover-foreground: 222.2 84% 4.9%;
123
+ --primary: 222.2 47.4% 11.2%;
124
+ --primary-foreground: 210 40% 98%;
125
+ --secondary: 210 40% 96.1%;
126
+ --secondary-foreground: 222.2 47.4% 11.2%;
127
+ --muted: 210 40% 96.1%;
128
+ --muted-foreground: 215.4 16.3% 46.9%;
129
+ --accent: 210 40% 96.1%;
130
+ --accent-foreground: 222.2 47.4% 11.2%;
131
+ --destructive: 0 84.2% 60.2%;
132
+ --destructive-foreground: 210 40% 98%;
133
+ --border: 214.3 31.8% 91.4%;
134
+ --input: 214.3 31.8% 91.4%;
135
+ --ring: 222.2 84% 4.9%;
136
+ --radius: 0.5rem;
137
+ }
138
+
139
+ .dark {
140
+ --background: 222.2 84% 4.9%;
141
+ --foreground: 210 40% 98%;
142
+ --card: 222.2 84% 4.9%;
143
+ --card-foreground: 210 40% 98%;
144
+ --popover: 222.2 84% 4.9%;
145
+ --popover-foreground: 210 40% 98%;
146
+ --primary: 210 40% 98%;
147
+ --primary-foreground: 222.2 47.4% 11.2%;
148
+ --secondary: 217.2 32.6% 17.5%;
149
+ --secondary-foreground: 210 40% 98%;
150
+ --muted: 217.2 32.6% 17.5%;
151
+ --muted-foreground: 215 20.2% 65.1%;
152
+ --accent: 217.2 32.6% 17.5%;
153
+ --accent-foreground: 210 40% 98%;
154
+ --destructive: 0 62.8% 30.6%;
155
+ --destructive-foreground: 210 40% 98%;
156
+ --border: 217.2 32.6% 17.5%;
157
+ --input: 217.2 32.6% 17.5%;
158
+ --ring: 212.7 26.8% 83.9%;
159
+ }
136
160
  }
137
161
  ```
138
162
 
139
163
  ## Components
140
164
 
141
- ### AdvancedCombobox
165
+ ### Breadcrumbs
142
166
 
143
- A searchable dropdown component with single or multiple selection support.
167
+ A navigation component that helps users understand their current location within a website's hierarchy.
144
168
 
145
- #### Features
169
+ #### Props
146
170
 
147
- - Single or multiple item selection
148
- - Search functionality with fuzzy matching
149
- - Keyboard navigation support (↑↓ to navigate, Enter to select)
150
- - Clear selection option
151
- - Customizable styling
152
- - Accessible by default (WAI-ARIA compliant)
171
+ | Prop | Type | Default | Description
172
+ |-----|-----|-----|-----
173
+ | segments | `Array<{ label: string; href?: string }>` | Required | Array of breadcrumb segments
174
+ | separator | `React.ReactNode` | `<ChevronRight className="h-4 w-4" />` | Custom separator between breadcrumb items
175
+ | className | `string` | - | Additional CSS classes
176
+ | onNavigate | `(href: string) => void` | - | Callback function when a breadcrumb is clicked
153
177
 
154
- #### Props
155
178
 
156
- | Prop | Type | Default | Description |
157
- | ----------- | ----------------------------------------- | -------- | ------------------------------------------ |
158
- | items | `Array<{ value: string, label: string }>` | Required | Array of items to display in the dropdown |
159
- | placeholder | `string` | Required | Placeholder text when no item is selected |
160
- | onSelect | `(value: string) => void` | Required | Callback function when an item is selected |
161
- | multiple | `boolean` | `false` | Enable multiple item selection |
162
- | searchable | `boolean` | `true` | Enable search functionality |
163
- | className | `string` | - | Additional CSS classes |
179
+ #### Example Usage
180
+
181
+ ```tsx
182
+ import { Breadcrumbs } from '@dsbtek/component-library';
183
+
184
+ function BreadcrumbsExample() {
185
+ return (
186
+ <Breadcrumbs
187
+ segments={[
188
+ { label: 'Home', href: '/' },
189
+ { label: 'Products', href: '/products' },
190
+ { label: 'Electronics', href: '/products/electronics' },
191
+ { label: 'Smartphones' },
192
+ ]}
193
+ onNavigate={(href) => console.log(`Navigating to: ${href}`)}
194
+ />
195
+ );
196
+ }
197
+ ```
164
198
 
165
- #### Keyboard Shortcuts
199
+ ### ColorPicker
200
+
201
+ A comprehensive color selection component with RGB, HSL support, color schemes, and history.
202
+
203
+ #### Props
204
+
205
+ | Prop | Type | Default | Description
206
+ |-----|-----|-----|-----
207
+ | color | `string` | `#000000` | Current color value in hex format
208
+ | onChange | `(value: string) => void` | - | Callback function when color changes
209
+ | className | `string` | - | Additional CSS classes
166
210
 
167
- - `↑/↓`: Navigate through items
168
- - `Enter`: Select highlighted item
169
- - `Esc`: Close dropdown
170
- - `Backspace`: Clear selection (when input is empty)
171
- - `Type`: Search items (when searchable is true)
172
211
 
173
212
  #### Example Usage
174
213
 
175
214
  ```tsx
176
- import { AdvancedCombobox } from '@dsbtek/component-library';
177
-
178
- // Single Selection with Search
179
- function SingleSelectExample() {
180
- return (
181
- <AdvancedCombobox
182
- items={[
183
- { value: '1', label: 'Option 1' },
184
- { value: '2', label: 'Option 2' },
185
- { value: '3', label: 'Option 3' },
186
- ]}
187
- placeholder="Select an option"
188
- onSelect={(value) => console.log('Selected:', value)}
189
- />
190
- );
215
+ import { ColorPicker } from '@dsbtek/component-library';
216
+ import { useState } from 'react';
217
+
218
+ function ColorPickerExample() {
219
+ const [color, setColor] = useState('#3B82F6');
220
+
221
+ return (
222
+ <ColorPicker
223
+ color={color}
224
+ onChange={setColor}
225
+ />
226
+ );
191
227
  }
228
+ ```
229
+
230
+ ### DataTable
231
+
232
+ A feature-rich table component with sorting, filtering, pagination, and more.
233
+
234
+ #### Props
192
235
 
193
- // Multiple Selection
194
- function MultipleSelectExample() {
195
- return (
196
- <AdvancedCombobox
197
- items={[
198
- { value: 'react', label: 'React' },
199
- { value: 'vue', label: 'Vue' },
200
- { value: 'angular', label: 'Angular' },
201
- ]}
202
- placeholder="Select frameworks"
203
- onSelect={(value) => console.log('Selected:', value)}
204
- multiple={true}
205
- />
206
- );
236
+ | Prop | Type | Default | Description
237
+ |-----|-----|-----|-----
238
+ | data | `T[]` | Required | Array of data items
239
+ | columns | `ColumnDef<T>[]` | Required | Array of column definitions
240
+ | meta | `{ current_page: number; last_page: number; per_page: number; total: number }` | - | Pagination metadata
241
+ | loading | `boolean` | `false` | Loading state of the table
242
+ | onPageChange | `(page: number) => void` | - | Callback when page changes
243
+ | onPerPageChange | `(perPage: number) => void` | - | Callback when items per page changes
244
+ | onSort | `(column: string, direction: 'asc' | 'desc' | null) => void` | - | Callback when sorting changes
245
+ | onSearch | `(value: string) => void` | - | Callback when search value changes
246
+ | onFilter | `(filters: AdvancedFilter[]) => void` | - | Callback when filters change
247
+ | pageSizeOptions | `number[]` | `[10,20,50]` | Available options for items per page
248
+ | renderItemActions | `(row: T) => React.ReactNode` | - | Function to render action buttons for each row
249
+ | features | `Partial<DataTableFeatures>` | - | Object to enable/disable various table features
250
+
251
+
252
+ #### Example Usage
253
+
254
+ ```tsx
255
+ import { DataTable } from '@dsbtek/component-library';
256
+
257
+ function DataTableExample() {
258
+ const columns = [
259
+ { accessorKey: 'name', header: 'Name' },
260
+ { accessorKey: 'email', header: 'Email' },
261
+ { accessorKey: 'role', header: 'Role' },
262
+ ];
263
+
264
+ const data = [
265
+ { id: '1', name: 'John Doe', email: 'john@example.com', role: 'Admin' },
266
+ { id: '2', name: 'Jane Smith', email: 'jane@example.com', role: 'User' },
267
+ // ... more data
268
+ ];
269
+
270
+ return (
271
+ <DataTable
272
+ data={data}
273
+ columns={columns}
274
+ features={{
275
+ sorting: true,
276
+ pagination: true,
277
+ search: true,
278
+ }}
279
+ onPageChange={(page) => console.log(`Page changed to ${page}`)}
280
+ onSort={(column, direction) => console.log(`Sorting ${column} ${direction}`)}
281
+ />
282
+ );
207
283
  }
208
284
  ```
209
285
 
210
- ### AdvancedDataTable
286
+ ### DateTimePicker
287
+
288
+ A versatile date and time selection component with support for ranges, time-only, and date-only modes.
211
289
 
212
- A feature-rich data table component with sorting, filtering, and pagination.
290
+ #### Props
213
291
 
214
- #### Features
292
+ | Prop | Type | Default | Description
293
+ |-----|-----|-----|-----
294
+ | date | `DateValue | DateRange | undefined | null` | - | Selected date(s)
295
+ | setDate | `(date: DateValue | DateRange | undefined) => void` | Required | Callback function when date changes
296
+ | isRange | `boolean` | `false` | Enable date range selection
297
+ | includeTime | `boolean` | `true` | Include time selection
298
+ | dateOnly | `boolean` | `false` | Show date picker only
299
+ | timeOnly | `boolean` | `false` | Show time picker only
300
+ | minDate | `Date` | - | Minimum selectable date
301
+ | maxDate | `Date` | - | Maximum selectable date
302
+ | disabledDates | `Date[]` | - | Array of disabled dates
303
+ | clearable | `boolean` | `true` | Allow clearing the selection
304
+ | disabled | `boolean` | `false` | Disable the input
215
305
 
216
- - Column sorting (click column headers)
217
- - Text filtering
218
- - Pagination with customizable page sizes
219
- - Row selection with checkboxes
220
- - Responsive design
221
- - Customizable styling
222
- - Loading states
223
- - Empty state handling
306
+
307
+ #### Example Usage
308
+
309
+ ```tsx
310
+ import { DateTimePicker } from '@dsbtek/component-library';
311
+ import { useState } from 'react';
312
+
313
+ function DateTimePickerExample() {
314
+ const [date, setDate] = useState<Date | undefined>(new Date());
315
+
316
+ return (
317
+ <DateTimePicker
318
+ date={date}
319
+ setDate={setDate}
320
+ includeTime={true}
321
+ />
322
+ );
323
+ }
324
+ ```
325
+
326
+ ### FileInput
327
+
328
+ A file upload component with drag and drop support, previews, and progress indication.
224
329
 
225
330
  #### Props
226
331
 
227
- | Prop | Type | Default | Description |
228
- | ------------------- | ---------------------------------------------------- | ------------ | -------------------------------------------------- |
229
- | columns | `Array<Column<T>>` | Required | Array of column definitions |
230
- | fetchData | `(params: FetchParams) => Promise<FetchResponse<T>>` | Required | Function to fetch data with pagination and sorting |
231
- | itemsPerPageOptions | `number[]` | `[10,20,50]` | Available options for items per page |
232
- | className | `string` | - | Additional CSS classes |
233
-
234
- #### Column Definition
235
-
236
- | Property | Type | Default | Description |
237
- | ---------- | ------------------------------------------- | -------- | ---------------------------------------- |
238
- | accessor | `keyof T \| ((item: T) => React.ReactNode)` | Required | Key or function to access the cell value |
239
- | header | `string` | Required | Column header text |
240
- | sortable | `boolean` | `false` | Enable column sorting |
241
- | filterable | `boolean` | `false` | Enable column filtering |
242
- | cell | `(value: any, item: T) => React.ReactNode` | - | Custom cell renderer |
243
-
244
- #### FetchParams Type
245
-
246
- ```typescript
247
- interface FetchParams {
248
- page: number;
249
- perPage: number;
250
- sortBy?: string;
251
- sortOrder?: 'asc' | 'desc';
252
- filters?: Record<string, string>;
332
+ | Prop | Type | Default | Description
333
+ |-----|-----|-----|-----
334
+ | value | `FileWithPreview[]` | - | Array of selected files
335
+ | onChange | `(files: FileWithPreview[]) => void` | Required | Callback when files change
336
+ | multiple | `boolean` | `false` | Allow multiple file selection
337
+ | accept | `Record<string, string[]>` | `{ "image/*": [".png", ".jpg", ".jpeg", ".gif"], "application/pdf": [".pdf"] }` | Accepted file types
338
+ | maxSize | `number` | 2MB | Maximum file size in bytes
339
+ | maxFiles | `number` | 5 | Maximum number of files
340
+ | disabled | `boolean` | `false` | Disable the input
341
+ | loading | `boolean` | `false` | Show loading state
342
+ | progress | `number | number[]` | - | Upload progress percentage
343
+ | onRemove | `(file: FileWithPreview) => void` | - | Callback when a file is removed
344
+
345
+
346
+ #### Example Usage
347
+
348
+ ```tsx
349
+ import { FileInput } from '@dsbtek/component-library';
350
+ import { useState } from 'react';
351
+
352
+ function FileInputExample() {
353
+ const [files, setFiles] = useState([]);
354
+
355
+ return (
356
+ <FileInput
357
+ value={files}
358
+ onChange={setFiles}
359
+ multiple={true}
360
+ maxSize={5 * 1024 * 1024} // 5MB
361
+ />
362
+ );
253
363
  }
254
364
  ```
255
365
 
256
- #### FetchResponse Type
257
-
258
- ```typescript
259
- interface FetchResponse<T> {
260
- data: T[];
261
- meta: {
262
- current_page: number;
263
- last_page: number;
264
- per_page: number;
265
- total: number;
266
- };
366
+ ### MultiSelect
367
+
368
+ A flexible select component that supports single or multiple selection with grouping.
369
+
370
+ #### Props
371
+
372
+ | Prop | Type | Default | Description
373
+ |-----|-----|-----|-----
374
+ | options | `OptionType[]` | Required | Array of selectable options
375
+ | selected | `string[]` | Required | Array of selected option values
376
+ | onChange | `(value: string[]) => void` | Required | Callback when selection changes
377
+ | placeholder | `string` | `"Select..."` | Placeholder text
378
+ | className | `string` | - | Additional CSS classes
379
+ | multiple | `boolean` | `false` | Allow multiple selection
380
+ | onLoadMore | `() => void` | - | Callback for infinite loading
381
+ | hasMore | `boolean` | `false` | Indicates if more options can be loaded
382
+ | isDialog | `boolean` | `false` | Use dialog instead of drawer on mobile
383
+
384
+
385
+ #### Example Usage
386
+
387
+ ```tsx
388
+ import { MultiSelect } from '@dsbtek/component-library';
389
+ import { useState } from 'react';
390
+
391
+ function MultiSelectExample() {
392
+ const [selected, setSelected] = useState(['react']);
393
+
394
+ const options = [
395
+ { label: 'React', value: 'react', group: 'Frontend' },
396
+ { label: 'Vue', value: 'vue', group: 'Frontend' },
397
+ { label: 'Angular', value: 'angular', group: 'Frontend' },
398
+ { label: 'Node.js', value: 'nodejs', group: 'Backend' },
399
+ { label: 'Express', value: 'express', group: 'Backend' },
400
+ ];
401
+
402
+ return (
403
+ <MultiSelect
404
+ options={options}
405
+ selected={selected}
406
+ onChange={setSelected}
407
+ placeholder="Select technologies..."
408
+ multiple={true}
409
+ />
410
+ );
267
411
  }
268
412
  ```
269
413
 
414
+ ### MultiStepper
415
+
416
+ A comprehensive multi-step form component with navigation, validation, and customizable steps.
417
+
418
+ #### Props
419
+
420
+ | Prop | Type | Default | Description
421
+ |-----|-----|-----|-----
422
+ | context | `React.Context<UseMultiStepFormTypeOptions<T>>` | Required | Context created with buildMultiStepForm
423
+ | previousLabel | `string` | `"Previous"` | Label for the previous button
424
+ | nextLabel | `string` | `"Next"` | Label for the next button
425
+ | endStepLabel | `string` | `"Submit"` | Label for the submit button
426
+ | showNavbar | `boolean` | `true` | Show the step navigation bar
427
+ | showButtons | `boolean` | `true` | Show the navigation buttons
428
+ | isLoading | `boolean` | `false` | Loading state for the submit button
429
+ | className | `string` | `""` | Additional CSS classes
430
+ | debug | `boolean` | `false` | Show debug information
431
+
432
+ #### Utility Functions
433
+
434
+ | Function | Description
435
+ |-----|-----
436
+ | buildMultiStepForm | Creates a context and provider for a multi-step form
437
+ | useMultiStepForm | Hook for accessing multi-step form functionality
438
+
270
439
  #### Example Usage
271
440
 
272
441
  ```tsx
273
- import { AdvancedDataTable } from '@dsbtek/component-library';
442
+ import {
443
+ MultiStepper,
444
+ buildMultiStepForm,
445
+ useMultiStepForm,
446
+ Form
447
+ } from '@dsbtek/component-library';
448
+ import { z } from 'zod';
449
+ import { useForm, FormProvider, useFormContext } from 'react-hook-form';
450
+ import {
451
+ FormField,
452
+ FormItem,
453
+ FormLabel,
454
+ FormControl,
455
+ FormMessage
456
+ } from '@/components/ui/form';
457
+ import { Input } from '@/components/ui/input';
458
+ import { PasswordInput } from '@dsbtek/component-library';
459
+ import { MultiSelect } from '@dsbtek/component-library';
460
+ import { FileInput } from '@dsbtek/component-library';
461
+
462
+ // Define your form schema
463
+ const signupSchema = z.object({
464
+ name: z.string().min(2, "Name must be at least 2 characters"),
465
+ email: z.string().email("Please enter a valid email"),
466
+ phone: z.string().min(10, "Please enter a valid phone number"),
467
+ password: z.string().min(8, "Password must be at least 8 characters"),
468
+ password_confirmation: z.string(),
469
+ roles: z.array(z.string()),
470
+ permissions: z.array(z.string()),
471
+ image: z.any().optional(),
472
+ }).refine((data) => data.password === data.password_confirmation, {
473
+ message: "Passwords do not match",
474
+ path: ["password_confirmation"],
475
+ });
476
+
477
+ // Step 1: Basic Information
478
+ function Step1() {
479
+ const { control } = useFormContext();
480
+
481
+ return (
482
+ <>
483
+ <FormField
484
+ control={control}
485
+ name="name"
486
+ render={({ field }) => (
487
+ <FormItem>
488
+ <FormLabel>Name</FormLabel>
489
+ <FormControl>
490
+ <Input placeholder="Enter your name" {...field} />
491
+ </FormControl>
492
+ <FormMessage />
493
+ </FormItem>
494
+ )}
495
+ />
496
+ <FormField
497
+ control={control}
498
+ name="email"
499
+ render={({ field }) => (
500
+ <FormItem>
501
+ <FormLabel>Email</FormLabel>
502
+ <FormControl>
503
+ <Input placeholder="Enter your email" {...field} />
504
+ </FormControl>
505
+ <FormMessage />
506
+ </FormItem>
507
+ )}
508
+ />
509
+ <FormField
510
+ control={control}
511
+ name="phone"
512
+ render={({ field }) => (
513
+ <FormItem>
514
+ <FormLabel>Phone</FormLabel>
515
+ <FormControl>
516
+ <Input placeholder="Enter your phone number" {...field} />
517
+ </FormControl>
518
+ <FormMessage />
519
+ </FormItem>
520
+ )}
521
+ />
522
+ </>
523
+ );
524
+ }
274
525
 
275
- function TableExample() {
276
- const columns = [
277
- {
278
- accessor: 'name',
279
- header: 'Name',
280
- sortable: true,
281
- filterable: true,
282
- },
283
- {
284
- accessor: 'email',
285
- header: 'Email',
286
- sortable: true,
287
- },
526
+ // Step 2: Security
527
+ function Step2() {
528
+ const { control } = useFormContext();
529
+
530
+ return (
531
+ <>
532
+ <FormField
533
+ control={control}
534
+ name="password"
535
+ render={({ field }) => (
536
+ <FormItem>
537
+ <FormLabel>Password</FormLabel>
538
+ <FormControl>
539
+ <PasswordInput placeholder="Enter your password" {...field} />
540
+ </FormControl>
541
+ <FormMessage />
542
+ </FormItem>
543
+ )}
544
+ />
545
+ <FormField
546
+ control={control}
547
+ name="password_confirmation"
548
+ render={({ field }) => (
549
+ <FormItem>
550
+ <FormLabel>Confirm Password</FormLabel>
551
+ <FormControl>
552
+ <PasswordInput placeholder="Confirm your password" {...field} />
553
+ </FormControl>
554
+ <FormMessage />
555
+ </FormItem>
556
+ )}
557
+ />
558
+ </>
559
+ );
560
+ }
561
+
562
+ // Step 3: Roles & Permissions
563
+ function Step3() {
564
+ const { control } = useFormContext();
565
+
566
+ const roleOptions = [
567
+ { label: 'Admin', value: 'admin' },
568
+ { label: 'User', value: 'user' },
569
+ { label: 'Editor', value: 'editor' },
570
+ ];
571
+
572
+ const permissionOptions = [
573
+ { label: 'Create', value: 'create', group: 'Content' },
574
+ { label: 'Edit', value: 'edit', group: 'Content' },
575
+ { label: 'Delete', value: 'delete', group: 'Content' },
576
+ { label: 'View Users', value: 'view_users', group: 'Users' },
577
+ { label: 'Manage Users', value: 'manage_users', group: 'Users' },
578
+ ];
579
+
580
+ return (
581
+ <>
582
+ <FormField
583
+ control={control}
584
+ name="roles"
585
+ render={({ field }) => (
586
+ <FormItem>
587
+ <FormLabel>Roles</FormLabel>
588
+ <FormControl>
589
+ <MultiSelect
590
+ options={roleOptions}
591
+ selected={field.value || []}
592
+ onChange={field.onChange}
593
+ placeholder="Select roles"
594
+ multiple={true}
595
+ />
596
+ </FormControl>
597
+ <FormMessage />
598
+ </FormItem>
599
+ )}
600
+ />
601
+ <FormField
602
+ control={control}
603
+ name="permissions"
604
+ render={({ field }) => (
605
+ <FormItem>
606
+ <FormLabel>Permissions</FormLabel>
607
+ <FormControl>
608
+ <MultiSelect
609
+ options={permissionOptions}
610
+ selected={field.value || []}
611
+ onChange={field.onChange}
612
+ placeholder="Select permissions"
613
+ multiple={true}
614
+ />
615
+ </FormControl>
616
+ <FormMessage />
617
+ </FormItem>
618
+ )}
619
+ />
620
+ </>
621
+ );
622
+ }
623
+
624
+ // Step 4: Profile Picture
625
+ function Step4() {
626
+ const { control } = useFormContext();
627
+
628
+ return (
629
+ <FormField
630
+ control={control}
631
+ name="image"
632
+ render={({ field }) => (
633
+ <FormItem>
634
+ <FormLabel>Profile Picture</FormLabel>
635
+ <FormControl>
636
+ <FileInput
637
+ value={field.value || []}
638
+ onChange={field.onChange}
639
+ accept={{ "image/*": [".png", ".jpg", ".jpeg"] }}
640
+ maxSize={2 * 1024 * 1024} // 2MB
641
+ />
642
+ </FormControl>
643
+ <FormMessage />
644
+ </FormItem>
645
+ )}
646
+ />
647
+ );
648
+ }
649
+
650
+ // Define your form steps
651
+ const forms: Form<z.infer<typeof signupSchema>>[] = [
652
+ { id: 1, label: "Basic Information", form: Step1, fields: ["name", "email", "phone"] },
653
+ { id: 2, label: "Security", form: Step2, fields: ["password", "password_confirmation"] },
654
+ { id: 3, label: "Roles & Permissions", form: Step3, fields: ["roles", "permissions"] },
655
+ { id: 4, label: "Profile Picture", form: Step4, fields: ["image"] },
656
+ ];
657
+
658
+ // Initial form data
659
+ const initialFormData = {
660
+ name: "",
661
+ email: "",
662
+ phone: "",
663
+ password: "",
664
+ password_confirmation: "",
665
+ roles: [],
666
+ permissions: [],
667
+ image: undefined,
668
+ };
669
+
670
+ // Create form context and provider
671
+ const { FormContext, FormProvider } = buildMultiStepForm(
288
672
  {
289
- accessor: 'status',
290
- header: 'Status',
291
- cell: (value) => (
292
- <span className={`status-${value.toLowerCase()}`}>{value}</span>
293
- ),
673
+ schema: signupSchema,
674
+ currentStep: 0,
675
+ setCurrentStep: () => {},
676
+ forms,
677
+ saveFormData: async (data) => {
678
+ // Submit your form data
679
+ console.log("Form submitted:", data);
680
+ return { success: true };
681
+ },
294
682
  },
295
- ];
296
-
297
- const fetchData = async (params) => {
298
- const response = await fetch(
299
- '/api/users?' +
300
- new URLSearchParams({
301
- page: params.page.toString(),
302
- perPage: params.perPage.toString(),
303
- sortBy: params.sortBy,
304
- sortOrder: params.sortOrder,
305
- ...params.filters,
306
- }),
307
- );
308
- return response.json();
309
- };
310
-
311
- return (
312
- <AdvancedDataTable
313
- columns={columns}
314
- fetchData={fetchData}
315
- itemsPerPageOptions={[5, 10, 20, 50]}
316
- />
317
- );
683
+ signupSchema,
684
+ initialFormData
685
+ );
686
+
687
+ // Use the multi-step form
688
+ function SignupForm() {
689
+ const [isLoading, setIsLoading] = useState(false);
690
+
691
+ const handleSubmit = async (data) => {
692
+ setIsLoading(true);
693
+ try {
694
+ // Submit your form data
695
+ await saveUserData(data);
696
+ toast.success("Account created successfully!");
697
+ } catch (error) {
698
+ toast.error("Failed to create account");
699
+ } finally {
700
+ setIsLoading(false);
701
+ }
702
+ };
703
+
704
+ return (
705
+ <FormProvider>
706
+ <div className="max-w-md mx-auto p-6 bg-card rounded-lg shadow-md">
707
+ <h1 className="text-2xl font-bold mb-6 text-center">Create Account</h1>
708
+ <MultiStepper
709
+ context={FormContext}
710
+ previousLabel="Back"
711
+ nextLabel="Continue"
712
+ endStepLabel="Sign Up"
713
+ isLoading={isLoading}
714
+ className="space-y-6"
715
+ />
716
+ </div>
717
+ </FormProvider>
718
+ );
719
+ }
720
+ ```
721
+
722
+ ### PasswordInput
723
+
724
+ A password input component with show/hide functionality.
725
+
726
+ #### Props
727
+
728
+ | Prop | Type | Default | Description
729
+ |-----|-----|-----|-----
730
+ | className | `string` | - | Additional CSS classes
731
+ | showPasswordLabel | `string` | `"Show password"` | Screen reader label for show password button
732
+ | hidePasswordLabel | `string` | `"Hide password"` | Screen reader label for hide password button
733
+ | ...props | `React.InputHTMLAttributes<HTMLInputElement>` | - | All standard input props
734
+
735
+
736
+ #### Example Usage
737
+
738
+ ```tsx
739
+ import { PasswordInput } from '@dsbtek/component-library';
740
+ import { useState } from 'react';
741
+
742
+ function PasswordInputExample() {
743
+ const [password, setPassword] = useState('');
744
+
745
+ return (
746
+ <PasswordInput
747
+ value={password}
748
+ onChange={(e) => setPassword(e.target.value)}
749
+ placeholder="Enter your password"
750
+ />
751
+ );
752
+ }
753
+ ```
754
+
755
+ ### PhoneInput
756
+
757
+ An international phone number input with country selection and validation.
758
+
759
+ #### Props
760
+
761
+ | Prop | Type | Default | Description
762
+ |-----|-----|-----|-----
763
+ | value | `string` | - | Phone number value
764
+ | defaultCountry | `CountryCode` | `"US"` | Default country code
765
+ | ...props | `React.ComponentPropsWithoutRef<'input'>` | - | All standard input props
766
+
767
+
768
+ #### Example Usage
769
+
770
+ ```tsx
771
+ import { PhoneInput, getPhoneData } from '@dsbtek/component-library';
772
+ import { useState, useEffect } from 'react';
773
+
774
+ function PhoneInputExample() {
775
+ const [phone, setPhone] = useState('+1');
776
+ const [phoneData, setPhoneData] = useState(getPhoneData(phone));
777
+
778
+ useEffect(() => {
779
+ setPhoneData(getPhoneData(phone));
780
+ }, [phone]);
781
+
782
+ return (
783
+ <PhoneInput
784
+ value={phone}
785
+ onChange={(e) => setPhone(e.target.value)}
786
+ defaultCountry="US"
787
+ />
788
+ );
789
+ }
790
+ ```
791
+
792
+ ### ResponsiveAlertDialog
793
+
794
+ An alert dialog component that adapts to desktop (modal) and mobile (drawer) views.
795
+
796
+ #### Props
797
+
798
+ | Prop | Type | Default | Description
799
+ |-----|-----|-----|-----
800
+ | trigger | `React.ReactNode` | Required | Element that triggers the dialog
801
+ | title | `string` | Required | Dialog title
802
+ | description | `string` | Required | Dialog description
803
+ | cancelText | `string` | `"Cancel"` | Text for the cancel button
804
+ | confirmText | `string` | `"Continue"` | Text for the confirm button
805
+ | onConfirm | `() => void` | Required | Callback when confirm button is clicked
806
+
807
+
808
+ #### Example Usage
809
+
810
+ ```tsx
811
+ import { ResponsiveAlertDialog } from '@dsbtek/component-library';
812
+ import { Button } from '@dsbtek/component-library';
813
+
814
+ function AlertDialogExample() {
815
+ const handleDelete = () => {
816
+ console.log('Item deleted');
817
+ };
818
+
819
+ return (
820
+ <ResponsiveAlertDialog
821
+ trigger={<Button variant="destructive">Delete Item</Button>}
822
+ title="Are you sure?"
823
+ description="This action cannot be undone. This will permanently delete the item."
824
+ cancelText="Cancel"
825
+ confirmText="Delete"
826
+ onConfirm={handleDelete}
827
+ />
828
+ );
829
+ }
830
+ ```
831
+
832
+ ### ResponsiveDialog
833
+
834
+ A dialog component that adapts to desktop (modal) and mobile (drawer) views.
835
+
836
+ #### Props
837
+
838
+ | Prop | Type | Default | Description
839
+ |-----|-----|-----|-----
840
+ | trigger | `React.ReactNode` | Required | Element that triggers the dialog
841
+ | title | `string` | Required | Dialog title
842
+ | description | `string` | Required | Dialog description
843
+ | children | `React.ReactNode` | Required | Dialog content
844
+
845
+
846
+ #### Example Usage
847
+
848
+ ```tsx
849
+ import { ResponsiveDialog } from '@dsbtek/component-library';
850
+ import { Button } from '@dsbtek/component-library';
851
+
852
+ function DialogExample() {
853
+ return (
854
+ <ResponsiveDialog
855
+ trigger={<Button>Open Dialog</Button>}
856
+ title="Edit Profile"
857
+ description="Make changes to your profile here."
858
+ >
859
+ <form className="space-y-4 pt-4">
860
+ <div className="space-y-2">
861
+ <label htmlFor="name">Name</label>
862
+ <input id="name" className="w-full p-2 border rounded" />
863
+ </div>
864
+ <div className="space-y-2">
865
+ <label htmlFor="email">Email</label>
866
+ <input id="email" type="email" className="w-full p-2 border rounded" />
867
+ </div>
868
+ <div className="flex justify-end space-x-2">
869
+ <Button type="button" variant="outline">Cancel</Button>
870
+ <Button type="submit">Save</Button>
871
+ </div>
872
+ </form>
873
+ </ResponsiveDialog>
874
+ );
875
+ }
876
+ ```
877
+
878
+ ### TagInput
879
+
880
+ A component for adding and managing tags with suggestions and validation.
881
+
882
+ #### Props
883
+
884
+ | Prop | Type | Default | Description
885
+ |-----|-----|-----|-----
886
+ | placeholder | `string` | `"Add tag..."` | Placeholder text
887
+ | tags | `string[]` | Required | Array of current tags
888
+ | setTags | `(tags: string[]) => void` | Required | Callback when tags change
889
+ | suggestions | `string[]` | `[]` | Array of tag suggestions
890
+ | maxTags | `number` | - | Maximum number of tags allowed
891
+ | disabled | `boolean` | `false` | Disable the input
892
+ | error | `string` | - | Error message to display
893
+
894
+
895
+ #### Example Usage
896
+
897
+ ```tsx
898
+ import { TagInput } from '@dsbtek/component-library';
899
+ import { useState } from 'react';
900
+
901
+ function TagInputExample() {
902
+ const [tags, setTags] = useState(['react', 'typescript']);
903
+
904
+ const suggestions = [
905
+ 'react', 'vue', 'angular', 'svelte', 'javascript',
906
+ 'typescript', 'node', 'express', 'mongodb'
907
+ ];
908
+
909
+ return (
910
+ <TagInput
911
+ tags={tags}
912
+ setTags={setTags}
913
+ suggestions={suggestions}
914
+ placeholder="Add technologies..."
915
+ />
916
+ );
318
917
  }
319
918
  ```
320
919
 
@@ -325,24 +924,30 @@ All components use Tailwind CSS for styling and can be customized using Tailwind
325
924
  ### Custom Styling Example
326
925
 
327
926
  ```tsx
328
- <AdvancedCombobox
329
- className="w-[300px] rounded-lg"
330
- items={items}
331
- placeholder="Custom styled combobox"
332
- onSelect={handleSelect}
927
+ <Breadcrumbs
928
+ className="text-blue-600 dark:text-blue-400"
929
+ segments={[
930
+ { label: 'Home', href: '/' },
931
+ { label: 'Products', href: '/products' },
932
+ { label: 'Current Page' },
933
+ ]}
333
934
  />
334
935
 
335
- <AdvancedDataTable
336
- className="border rounded-lg shadow-sm"
337
- columns={columns}
338
- fetchData={fetchData}
936
+ <ColorPicker
937
+ className="w-[300px] rounded-lg"
938
+ color="#3B82F6"
939
+ onChange={setColor}
339
940
  />
340
941
  ```
341
942
 
943
+ ## TypeScript Support
944
+
945
+ All components include full TypeScript support out of the box. Type definitions are automatically available when you import components.
946
+
342
947
  ## Contributing
343
948
 
344
- [Add contribution guidelines here]
949
+ Please read our [Contributing Guide](CONTRIBUTING.md) before submitting a Pull Request.
345
950
 
346
951
  ## License
347
952
 
348
- MIT
953
+ MIT © [Smartflowtech Team](https://smartflowtech.com)