@stackloop/ui 1.0.7

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 ADDED
@@ -0,0 +1,638 @@
1
+ ## Installation
2
+
3
+ ### NPM
4
+
5
+ ```bash
6
+ npm install --save @stackloop/ui
7
+ ```
8
+
9
+ ### Peer Dependencies
10
+
11
+ Ensure `react` and `react-dom` (>=18) are installed.
12
+
13
+ ### Next.js Setup
14
+
15
+ #### 1. Import Theme CSS
16
+
17
+ Import the library CSS in your root layout or `_app.tsx`:
18
+
19
+ ```tsx
20
+ // app/layout.tsx (App Router)
21
+ import '@stackloop/ui/theme.css'
22
+
23
+ export default function RootLayout({ children }) {
24
+ return (
25
+ <html lang="en">
26
+ <body>{children}</body>
27
+ </html>
28
+ )
29
+ }
30
+ ```
31
+
32
+ ```tsx
33
+ // pages/_app.tsx (Pages Router)
34
+ import '@stackloop/ui/theme.css'
35
+
36
+ export default function App({ Component, pageProps }) {
37
+ return <Component {...pageProps} />
38
+ }
39
+ ```
40
+
41
+ #### 2. Tailwind Configuration
42
+
43
+ If using Tailwind CSS v4 with `@theme`, the library's theme variables are already configured. If you need to customize colors:
44
+
45
+ ```css
46
+ /* In your global CSS or theme.css */
47
+ @theme {
48
+ /* Override library colors */
49
+ --color-primary: #your-color;
50
+ --color-primary-dark: #your-darker-color;
51
+ --color-border: #your-border-color;
52
+ --color-border-dark: #your-darker-border;
53
+ --color-secondary: #your-secondary-bg;
54
+ --color-background: #your-bg-color;
55
+ --color-foreground: #your-text-color;
56
+
57
+ /* Semantic colors */
58
+ --color-success: #10b981;
59
+ --color-warning: #f59e0b;
60
+ --color-error: #ef4444;
61
+ --color-info: #3b82f6;
62
+ }
63
+ ```
64
+
65
+ ### Usage with React (Vite/CRA)
66
+
67
+ ```js
68
+ // In your main entry file (main.tsx or index.tsx)
69
+ import '@stackloop/ui/theme.css'
70
+ ```
71
+
72
+ ## Importing Components
73
+
74
+ Import components from the package root:
75
+
76
+ ```tsx
77
+ import { Button, Modal, Input } from '@stackloop/ui'
78
+ ```
79
+
80
+ All components are **client-side** components with `'use client'` directive, making them compatible with Next.js App Router.
81
+
82
+ ## Theme Customization
83
+
84
+ The library uses a simplified color system with semantic variables:
85
+
86
+ ### Color Variables
87
+
88
+ | Variable | Default | Description |
89
+ |----------|---------|-------------|
90
+ | `--color-primary` | `#525252` | Primary brand color |
91
+ | `--color-primary-dark` | `#404040` | Darker primary variant |
92
+ | `--color-border` | `#e5e5e5` | Default border color |
93
+ | `--color-border-dark` | `#d4d4d4` | Darker border variant |
94
+ | `--color-secondary` | `#fafafa` | Secondary background |
95
+ | `--color-background` | `#ffffff` | Main background |
96
+ | `--color-foreground` | `#171717` | Primary text color |
97
+ | `--color-success` | `#10b981` | Success state |
98
+ | `--color-warning` | `#f59e0b` | Warning state |
99
+ | `--color-error` | `#ef4444` | Error state |
100
+ | `--color-info` | `#3b82f6` | Info state |
101
+
102
+ ### Customizing Colors
103
+
104
+ Create a custom theme file or extend the existing one:
105
+
106
+ ```css
107
+ /* styles/custom-theme.css */
108
+ @import '@stackloop/ui/theme.css';
109
+
110
+ @theme {
111
+ /* Brand colors */
112
+ --color-primary: #3b82f6;
113
+ --color-primary-dark: #2563eb;
114
+
115
+ /* Borders */
116
+ --color-border: #e2e8f0;
117
+ --color-border-dark: #cbd5e1;
118
+
119
+ /* Backgrounds */
120
+ --color-secondary: #f8fafc;
121
+ --color-background: #ffffff;
122
+ --color-foreground: #0f172a;
123
+ }
124
+ ```
125
+
126
+ ### Dark Mode Support
127
+
128
+ You can add dark mode variants:
129
+
130
+ ```css
131
+ @theme {
132
+ /* Light mode (default) */
133
+ --color-background: #ffffff;
134
+ --color-foreground: #171717;
135
+
136
+ /* Dark mode */
137
+ @media (prefers-color-scheme: dark) {
138
+ --color-background: #0a0a0a;
139
+ --color-foreground: #fafafa;
140
+ --color-primary: #60a5fa;
141
+ --color-border: #27272a;
142
+ }
143
+ }
144
+ ```
145
+
146
+ ## Components Reference
147
+
148
+ - For each component below you'll find a short description, props (type, default, notes) and a minimal usage example.
149
+
150
+ **Checkbox**:
151
+ - **Description:** Accessible checkbox with optional label and description.
152
+ - **Props:**
153
+ - **`label`**: `string` — optional.
154
+ - **`description`**: `string` — optional.
155
+ - **`onChange`**: `(checked: boolean) => void` — optional.
156
+ - **`className`**: `string` — optional.
157
+ - Inherits standard `input` props (e.g. `disabled`, `defaultChecked`, `checked`).
158
+ - **Usage:**
159
+
160
+ ```jsx
161
+ import { Checkbox } from '@stackloop/ui'
162
+
163
+ <Checkbox label="Accept terms" onChange={(v) => console.log(v)} />
164
+ ```
165
+
166
+ **Button**:
167
+ - **Description:** Animated button with variants, sizes, icons and loading state.
168
+ - **Props:**
169
+ - **`variant`**: `'primary' | 'secondary' | 'outline' | 'ghost' | 'danger'` — default: `'primary'`.
170
+ - **`size`**: `'sm' | 'md' | 'lg'` — default: `'md'`.
171
+ - **`loading`**: `boolean` — default: `false`.
172
+ - **`icon`**: `ReactNode` — optional.
173
+ - **`className`**: `string` — optional.
174
+ - Inherits standard `button` props.
175
+ - **Usage:**
176
+
177
+ ```jsx
178
+ import { Button } from '@stackloop/ui'
179
+
180
+ <Button variant="outline" size="lg" onClick={() => {}}>Save</Button>
181
+ ```
182
+
183
+ **Input**:
184
+ - **Description:** Text input with label, error and optional icons.
185
+ - **Props:**
186
+ - **`label`**: `string` — optional.
187
+ - **`error`**: `string` — optional.
188
+ - **`hint`**: `string` — optional.
189
+ - **`leftIcon`** / **`rightIcon`**: `ReactNode` — optional.
190
+ - **`className`**: `string` — optional.
191
+ - Inherits `input` HTML attributes.
192
+ - **Usage:**
193
+
194
+ ```jsx
195
+ import { Input } from '@stackloop/ui'
196
+
197
+ <Input label="Email" placeholder="you@example.com" />
198
+ ```
199
+
200
+ **Modal**:
201
+ - **Description:** Centered modal with backdrop, title and Escape-to-close handling.
202
+ - **Props:**
203
+ - **`isOpen`**: `boolean` — required.
204
+ - **`onClose`**: `() => void` — required.
205
+ - **`children`**: `ReactNode` — required.
206
+ - **`title`**: `string` — optional.
207
+ - **`size`**: `'sm'|'md'|'lg'|'xl'|'full'` — default: `'md'`.
208
+ - **`className`**: `string` — optional.
209
+ - **Subcomponents:** `ModalContent`, `ModalFooter`.
210
+ - **Usage:**
211
+
212
+ ```jsx
213
+ import { Modal, ModalContent, ModalFooter } from '@stackloop/ui'
214
+
215
+ <Modal isOpen={open} onClose={() => setOpen(false)} title="My Modal">
216
+ <ModalContent>Body</ModalContent>
217
+ <ModalFooter>
218
+ <button onClick={() => setOpen(false)}>Close</button>
219
+ </ModalFooter>
220
+ </Modal>
221
+ ```
222
+
223
+ **Table**:
224
+ - **Description:** Generic sortable table component with loading skeleton, row interactions, and responsive design. Supports client-side sorting and custom cell rendering.
225
+ - **Props:**
226
+ - **`data`**: `T[]` — required. Array of data items to display in the table.
227
+ - **`columns`**: `Column<T>[]` — required. Array of column definitions (see Column interface below).
228
+ - **`loading`**: `boolean` — optional. Shows animated skeleton loading state when true.
229
+ - **`onRowClick`**: `(item: T) => void` — optional. Callback fired when a row is clicked. Adds hover effect and pointer cursor to rows.
230
+ - **`keyExtractor`**: `(item: T) => string` — required. Function to extract unique key from each data item for React reconciliation.
231
+ - **`className`**: `string` — optional. Additional CSS classes for the table wrapper.
232
+
233
+ - **Column Interface:**
234
+ ```typescript
235
+ interface Column<T> {
236
+ key: string; // Unique column identifier and default accessor key
237
+ header: string; // Column header text displayed in table header
238
+ sortable?: boolean; // Enable sorting for this column (default: false)
239
+ render?: (item: T) => ReactNode; // Custom render function for cell content
240
+ width?: string; // CSS width value (e.g., '100px', '20%', 'auto')
241
+ }
242
+ ```
243
+
244
+ - **Sorting Behavior:**
245
+ - Click sortable column headers to toggle between ascending → descending → no sort.
246
+ - Sort icons: `ChevronUp` (ascending), `ChevronDown` (descending), `ChevronsUpDown` (sortable but not active).
247
+ - Sorting is client-side using JavaScript's `localeCompare` for strings and numeric comparison for numbers.
248
+ - Only one column can be sorted at a time.
249
+
250
+ - **Loading State:**
251
+ - When `loading={true}`, displays skeleton rows with animated gradient shimmer.
252
+ - Shows 5 skeleton rows by default, preserving table structure and column widths.
253
+ - Loading state prevents interactions and hides real data.
254
+
255
+ - **Styling & Customization:**
256
+ - Responsive: Horizontal scroll on mobile, full table view on desktop.
257
+ - Hover states: Rows have hover background when `onRowClick` is provided.
258
+ - Colors: Uses semantic color tokens (`border`, `border-dark`, `background`, `foreground-color`, `primary`).
259
+ - Animations: Powered by Framer Motion for smooth row entry and sorting transitions.
260
+
261
+ - **Usage:**
262
+
263
+ ```jsx
264
+ import { Table } from '@stackloop/ui'
265
+
266
+ // Basic example
267
+ const columns = [
268
+ { key: 'id', header: 'ID', width: '80px' },
269
+ { key: 'name', header: 'Name', sortable: true },
270
+ {
271
+ key: 'status',
272
+ header: 'Status',
273
+ render: (item) => <Badge variant={item.status === 'active' ? 'success' : 'default'}>{item.status}</Badge>
274
+ }
275
+ ]
276
+
277
+ <Table
278
+ data={users}
279
+ columns={columns}
280
+ keyExtractor={(user) => String(user.id)}
281
+ onRowClick={(user) => navigate(`/users/${user.id}`)}
282
+ loading={isLoading}
283
+ />
284
+
285
+ // Advanced example with custom rendering
286
+ const columns = [
287
+ { key: 'avatar', header: '', width: '50px', render: (u) => <img src={u.avatar} /> },
288
+ { key: 'name', header: 'Full Name', sortable: true },
289
+ { key: 'email', header: 'Email Address', sortable: true },
290
+ {
291
+ key: 'createdAt',
292
+ header: 'Joined',
293
+ sortable: true,
294
+ render: (u) => new Date(u.createdAt).toLocaleDateString()
295
+ }
296
+ ]
297
+ ```
298
+
299
+ **Dropdown**:
300
+ - **Description:** Select with optional search, clear and icons.
301
+ - **Props:**
302
+ - **`options`**: `{ value: string; label: string; icon?: ReactNode }[]` — required.
303
+ - **`value`**: `string` — optional.
304
+ - **`onChange`**: `(value: string) => void` — required.
305
+ - **`placeholder`**: `string` — default: `'Select an option'`.
306
+ - **`label`**, **`error`**, **`searchable`** (default `false`), **`clearable`** (default `true`), **`disabled`**, **`className`**.
307
+ - **Usage:**
308
+
309
+ ```jsx
310
+ import { Dropdown } from '@stackloop/ui'
311
+
312
+ <Dropdown options={[{value:'a',label:'A'}]} value={val} onChange={setVal} searchable />
313
+ ```
314
+
315
+ **BottomSheet**:
316
+ - **Description:** Mobile bottom sheet with header and optional close button.
317
+ - **Props:**
318
+ - **`isOpen`**: `boolean` — required.
319
+ - **`onClose`**: `() => void` — required.
320
+ - **`title`**: `string` — optional.
321
+ - **`children`**: `ReactNode` — required.
322
+ - **`showCloseButton`**: `boolean` — default: `true`.
323
+ - **`className`**: `string` — optional.
324
+ - **Usage:**
325
+
326
+ ```jsx
327
+ import { BottomSheet } from '@stackloop/ui'
328
+
329
+ <BottomSheet isOpen={open} onClose={() => setOpen(false)} title="Actions">...</BottomSheet>
330
+ ```
331
+
332
+ **DatePicker**:
333
+ - **Description:** Date picker with month navigation and min/max options.
334
+ - **Props:**
335
+ - **`value`**: `Date` — optional.
336
+ - **`onChange`**: `(date: Date) => void` — required.
337
+ - **`label`**, **`placeholder`** (default `'Select date'`), **`error`**, **`disabled`**, **`minDate`**, **`maxDate`**, **`className`**.
338
+ - **Usage:**
339
+
340
+ ```jsx
341
+ import { DatePicker } from '@stackloop/ui'
342
+
343
+ <DatePicker value={date} onChange={setDate} />
344
+ ```
345
+
346
+ **DualSlider**:
347
+ - **Description:** Two linked sliders showing relative portions.
348
+ - **Props:**
349
+ - **`value1`**, **`value2`**: `number` — required.
350
+ - **`onChange`**: `(v1:number, v2:number)=>void` — required.
351
+ - **`label1`**, **`label2`**: `string` — required.
352
+ - **`min`** (default `0`), **`max`** (default `100`), **`step`** (default `1`), **`unit`** (default `'%')`, **`disabled`**, **`className`**.
353
+ - **Usage:**
354
+
355
+ ```jsx
356
+ import { DualSlider } from '@stackloop/ui'
357
+
358
+ <DualSlider value1={30} value2={70} onChange={(a,b)=>{}} label1="A" label2="B" />
359
+ ```
360
+
361
+ **Pagination**:
362
+ - **Description:** Pagination with previous/next and numeric buttons.
363
+ - **Props:**
364
+ - **`currentPage`**: `number` — required.
365
+ - **`totalPages`**: `number` — required.
366
+ - **`onPageChange`**: `(page:number)=>void` — required.
367
+ - **`totalItems`**, **`itemsPerPage`**, **`className`** — optional.
368
+ - **Usage:**
369
+
370
+ ```jsx
371
+ import { Pagination } from '@stackloop/ui'
372
+
373
+ <Pagination currentPage={1} totalPages={10} onPageChange={setPage} />
374
+ ```
375
+
376
+ **Badge**:
377
+ - **Description:** Inline badge with color variants and optional dot.
378
+ - **Props:**
379
+ - **`children`**: `ReactNode` — required.
380
+ - **`variant`**: `'default'|'primary'|'success'|'warning'|'danger'|'info'` — default: `'default'`.
381
+ - **`size`**: `'sm'|'md'|'lg'` — default: `'md'`.
382
+ - **`dot`**: `boolean` — default: `false`.
383
+ - **`className`**: `string` — optional.
384
+ - **Usage:**
385
+
386
+ ```jsx
387
+ import { Badge } from '@stackloop/ui'
388
+
389
+ <Badge variant="primary">New</Badge>
390
+ ```
391
+
392
+ **FloatingActionButton (FAB)**:
393
+ - **Description:** Floating action button with expanded action list support.
394
+ - **Props:**
395
+ - **`icon`**, **`label`**, **`onClick`**, **`actions`** (array of `{label, icon, onClick, variant}`) — optional.
396
+ - **`variant`**: `'primary'|'secondary'` — default: `'primary'`.
397
+ - **`position`**: `'bottom-right'|'bottom-left'|'bottom-center'` — default: `'bottom-right'`.
398
+ - **`disabled`**, **`className`**.
399
+ - **Usage:**
400
+
401
+ ```jsx
402
+ import { FloatingActionButton as FAB } from '@stackloop/ui'
403
+
404
+ <FAB label="New" onClick={()=>{}} />
405
+ ```
406
+
407
+ **AudioRecorder**:
408
+ - **Description:** Browser audio recorder component using MediaRecorder.
409
+ - **Props:**
410
+ - **`onRecordingComplete`**: `(audioBlob: Blob) => void` — required.
411
+ - **`label`**: `string` — default: `'Record Audio'`.
412
+ - **`maxDuration`**: `number` — default: `300` seconds.
413
+ - **`disabled`**, **`className`**.
414
+ - **Usage:**
415
+
416
+ ```jsx
417
+ import { AudioRecorder } from '@stackloop/ui'
418
+
419
+ <AudioRecorder onRecordingComplete={(blob)=>{ /* handle */ }} />
420
+ ```
421
+
422
+ **Drawer**:
423
+ - **Description:** Side drawer that slides in from the left or right.
424
+ - **Props:**
425
+ - **`isOpen`**: `boolean` — required.
426
+ - **`onClose`**: `() => void` — required.
427
+ - **`children`**: `ReactNode` — required.
428
+ - **`title`**: `string` — optional.
429
+ - **`position`**: `'left'|'right'` — default: `'right'`.
430
+ - **`className`**: `string` — optional.
431
+ - **Usage:**
432
+
433
+ ```jsx
434
+ import { Drawer } from '@stackloop/ui'
435
+
436
+ <Drawer isOpen={open} onClose={()=>setOpen(false)} title="Menu">...</Drawer>
437
+ ```
438
+
439
+ **Toggle**:
440
+ - **Description:** Switch control with label and description.
441
+ - **Props:**
442
+ - **`label`**: `string` — optional.
443
+ - **`description`**: `string` — optional.
444
+ - **`onChange`**: `(checked:boolean)=>void` — optional.
445
+ - **`className`**: `string` — optional.
446
+ - Inherits input attributes such as `checked`, `disabled`.
447
+ - **Usage:**
448
+
449
+ ```jsx
450
+ import { Toggle } from '@stackloop/ui'
451
+
452
+ <Toggle label="Enable" onChange={(v)=>console.log(v)} />
453
+ ```
454
+
455
+ **StatusBadges** (OfflineBadge, SyncIndicator):
456
+ - **OfflineBadge Props:** `isOffline: boolean` (required), `className?: string`.
457
+ - **SyncIndicator Props:** `status: 'synced'|'syncing'|'unsynced'|'error'` (required), `count?: number`, `className?: string`.
458
+ - **Usage:**
459
+
460
+ ```jsx
461
+ import { OfflineBadge, SyncIndicator } from '@stackloop/ui'
462
+
463
+ <OfflineBadge isOffline={true} />
464
+ <SyncIndicator status="syncing" />
465
+ ```
466
+
467
+ **Slider**:
468
+ - **Description:** Single-value slider with optional unit and label.
469
+ - **Props:**
470
+ - **`value`**: `number` — required.
471
+ - **`onChange`**: `(value:number)=>void` — required.
472
+ - **`min`** (default `0`), **`max`** (default `100`), **`step`** (default `1`), **`label`**, **`showValue`** (default `true`), **`unit`** (default `'%')`, **`disabled`**, **`className`**.
473
+ - **Usage:**
474
+
475
+ ```jsx
476
+ import { Slider } from '@stackloop/ui'
477
+
478
+ <Slider value={50} onChange={setValue} />
479
+ ```
480
+
481
+ **RadioPills**:
482
+ - **Description:** Radio group styled as pill buttons.
483
+ - **Props:**
484
+ - **`options`**: `{ value: string; label: string; icon?: ReactNode }[]` — required.
485
+ - **`value`**: `string` — optional.
486
+ - **`onChange`**: `(v:string)=>void` — optional.
487
+ - **`name`**: `string` — required.
488
+ - **`disabled`**: `boolean` — optional.
489
+ - **`className`**: `string` — optional.
490
+ - **Usage:**
491
+
492
+ ```jsx
493
+ import { RadioPills } from '@stackloop/ui'
494
+
495
+ <RadioPills name="mode" options={[{value:'a',label:'A'}]} />
496
+ ```
497
+
498
+ **Textarea**:
499
+ - **Description:** Multiline input with label, error and helper text.
500
+ - **Props:**
501
+ - **`label`**: `string` — optional.
502
+ - **`error`**: `string` — optional.
503
+ - **`helperText`**: `string` — optional.
504
+ - **`className`**: `string` — optional.
505
+ - Inherits native `textarea` attributes.
506
+ - **Usage:**
507
+
508
+ ```jsx
509
+ import { Textarea } from '@stackloop/ui'
510
+
511
+ <Textarea label="Message" helperText="Max 500 chars" />
512
+ ```
513
+
514
+ **ThumbnailGrid**:
515
+ - **Description:** Grid to display thumbnails for images, documents, audio with optional remove/view actions.
516
+ - **Props:**
517
+ - **`items`**: `{ id, name, url, type:'image'|'document'|'audio', size? }[]` — required.
518
+ - **`onRemove`**: `(id:string)=>void` — optional.
519
+ - **`onView`**: `(item)=>void` — optional.
520
+ - **`columns`**: `2|3|4` — default: `3`.
521
+ - **`className`**.
522
+ - **Usage:**
523
+
524
+ ```jsx
525
+ import { ThumbnailGrid } from '@stackloop/ui'
526
+
527
+ <ThumbnailGrid items={items} onRemove={(id)=>{}} />
528
+ ```
529
+
530
+ **Card**:
531
+ - **Description:** Generic card container with variants and content subcomponents.
532
+ - **Props:**
533
+ - **`children`**: `ReactNode` — required.
534
+ - **`variant`**: `'default'|'outlined'|'elevated'` — default: `'default'`.
535
+ - **`padding`**: `'sm'|'md'|'lg'|'none'` — default: `'md'`.
536
+ - **`onClick`**: `() => void` — optional.
537
+ - **`hover`**: `boolean` — default: `false`.
538
+ - **`className`**: `string` — optional.
539
+ - **Subcomponents:** `CardHeader`, `CardTitle`, `CardDescription`, `CardContent`.
540
+ - **Usage:**
541
+
542
+ ```jsx
543
+ import { Card, CardTitle, CardContent } from '@stackloop/ui'
544
+
545
+ <Card variant="elevated"> <CardTitle>Title</CardTitle> <CardContent>Body</CardContent> </Card>
546
+ ```
547
+
548
+ **FileUpload (CameraCapture, FileUploader)**:
549
+ - **CameraCapture Props:** `onCapture(file:File) => void` (required), `onRemove?`, `preview?`, `label?`, `disabled?`, `className?`.
550
+ - **FileUploader Props:** `onUpload(files:File[]) => void` (required), `accept?` (default `'*/*'`), `multiple?` (default `false`), `label?`, `disabled?`, `className?`.
551
+ - **Usage:**
552
+
553
+ ```jsx
554
+ import { FileUploader, CameraCapture } from '@stackloop/ui'
555
+
556
+ <FileUploader onUpload={(files)=>{}} multiple accept="image/*" />
557
+ <CameraCapture onCapture={(file)=>{}} />
558
+ ```
559
+
560
+ **StepProgress**:
561
+ - **Description:** Horizontal stepper (desktop) and compact dots view (mobile) showing progress.
562
+ - **Props:**
563
+ - **`steps`**: `{ label: string; description?: string }[]` — required.
564
+ - **`currentStep`**: `number` — required.
565
+ - **`className`**: optional.
566
+ - **Usage:**
567
+
568
+ ```jsx
569
+ import { StepProgress } from '@stackloop/ui'
570
+
571
+ <StepProgress steps={[{label:'One'},{label:'Two'}]} currentStep={1} />
572
+ ```
573
+
574
+ ---
575
+
576
+ ## Next.js Best Practices
577
+
578
+ ### App Router
579
+
580
+ All components work seamlessly with Next.js 13+ App Router. They include the `'use client'` directive:
581
+
582
+ ```tsx
583
+ // app/page.tsx
584
+ import { Button, Card } from '@stackloop/ui'
585
+
586
+ export default function Page() {
587
+ return (
588
+ <div>
589
+ <Card>
590
+ <Button onClick={() => console.log('clicked')}>Click me</Button>
591
+ </Card>
592
+ </div>
593
+ )
594
+ }
595
+ ```
596
+
597
+ ### Server Components
598
+
599
+ To use these components with Server Components, import them in client components:
600
+
601
+ ```tsx
602
+ // components/ClientWrapper.tsx
603
+ 'use client'
604
+ import { Button } from '@stackloop/ui'
605
+
606
+ export function ClientButton() {
607
+ return <Button onClick={() => alert('Hello')}>Click</Button>
608
+ }
609
+ ```
610
+
611
+ ```tsx
612
+ // app/page.tsx (Server Component)
613
+ import { ClientButton } from '@/components/ClientWrapper'
614
+
615
+ export default function Page() {
616
+ return <ClientButton />
617
+ }
618
+ ```
619
+
620
+ ### Styling Considerations
621
+
622
+ - The library uses Tailwind CSS v4 with `@theme` directive
623
+ - Ensure your Next.js project is configured for Tailwind CSS v4
624
+ - All animations use Framer Motion and are optimized for performance
625
+
626
+ ## Browser Support
627
+
628
+ - Modern browsers (Chrome, Firefox, Safari, Edge)
629
+ - Mobile browsers (iOS Safari, Chrome Mobile)
630
+ - Audio recording requires MediaRecorder API support
631
+
632
+ ## License
633
+
634
+ MIT
635
+
636
+ ---
637
+
638
+ For questions, issues, or contributions, please visit the [GitHub repository](https://github.com/AtutiBonface/@stackloop/ui).
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+ export interface AudioRecorderProps {
3
+ onRecordingComplete: (audioBlob: Blob) => void;
4
+ label?: string;
5
+ maxDuration?: number;
6
+ disabled?: boolean;
7
+ className?: string;
8
+ }
9
+ export declare const AudioRecorder: React.FC<AudioRecorderProps>;
@@ -0,0 +1,12 @@
1
+ import React from 'react';
2
+ type BadgeVariant = 'default' | 'primary' | 'success' | 'warning' | 'danger' | 'info';
3
+ type BadgeSize = 'sm' | 'md' | 'lg';
4
+ export interface BadgeProps {
5
+ children: React.ReactNode;
6
+ variant?: BadgeVariant;
7
+ size?: BadgeSize;
8
+ className?: string;
9
+ dot?: boolean;
10
+ }
11
+ export declare const Badge: React.FC<BadgeProps>;
12
+ export {};
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ export interface BottomSheetProps {
3
+ isOpen: boolean;
4
+ onClose: () => void;
5
+ title?: string;
6
+ children: React.ReactNode;
7
+ showCloseButton?: boolean;
8
+ className?: string;
9
+ }
10
+ export declare const BottomSheet: React.FC<BottomSheetProps>;
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+ import { type HTMLMotionProps } from 'framer-motion';
3
+ export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
4
+ variant?: 'primary' | 'secondary' | 'outline' | 'ghost' | 'danger';
5
+ size?: 'sm' | 'md' | 'lg';
6
+ loading?: boolean;
7
+ icon?: React.ReactNode;
8
+ }
9
+ type MotionButtonProps = Omit<ButtonProps, 'children' | keyof HTMLMotionProps<'button'>> & {
10
+ children?: React.ReactNode;
11
+ } & HTMLMotionProps<'button'>;
12
+ export declare const Button: React.ForwardRefExoticComponent<Omit<MotionButtonProps, "ref"> & React.RefAttributes<HTMLButtonElement>>;
13
+ export {};