@stackloop/ui 1.0.10 → 2.0.1

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
@@ -181,7 +181,7 @@ You can add dark mode variants:
181
181
  ```
182
182
 
183
183
  **Input**:
184
- - **Description:** Text input with label, error and optional icons.
184
+ - **Description:** Text input with label, error and optional icons. Includes automatic password visibility toggle for password inputs.
185
185
  - **Props:**
186
186
  - **`label`**: `string` — optional.
187
187
  - **`error`**: `string` — optional.
@@ -189,12 +189,27 @@ You can add dark mode variants:
189
189
  - **`leftIcon`** / **`rightIcon`**: `ReactNode` — optional.
190
190
  - **`className`**: `string` — optional.
191
191
  - Inherits `input` HTML attributes.
192
+ - **Features:**
193
+ - **Password Toggle:** When `type="password"`, automatically displays an Eye/EyeOff icon to toggle password visibility. This overrides any custom `rightIcon`.
194
+ - **Icon Support:** Display icons on left or right side of input
195
+ - **Validation:** Built-in error and hint display
196
+ - **Accessibility:** Proper labels and ARIA attributes
192
197
  - **Usage:**
193
198
 
194
199
  ```jsx
195
200
  import { Input } from '@stackloop/ui'
196
201
 
197
202
  <Input label="Email" placeholder="you@example.com" />
203
+
204
+ // Password with automatic toggle
205
+ <Input label="Password" type="password" placeholder="Enter password" />
206
+
207
+ // With icons
208
+ <Input
209
+ label="Search"
210
+ leftIcon={<Search />}
211
+ placeholder="Search..."
212
+ />
198
213
  ```
199
214
 
200
215
  **Modal**:
@@ -436,7 +451,7 @@ You can add dark mode variants:
436
451
  ```
437
452
 
438
453
  **Dropdown**:
439
- - **Description:** Select with optional search, clear and icons.
454
+ - **Description:** General-purpose select component with optional search, clear, and icons. Use this for general UI selections outside of forms. For form-specific needs with validation, use the **Select** component instead.
440
455
  - **Props:**
441
456
  - **`options`**: `{ value: string; label: string; icon?: ReactNode }[]` — required.
442
457
  - **`value`**: `string` — optional.
@@ -451,6 +466,96 @@ You can add dark mode variants:
451
466
  <Dropdown options={[{value:'a',label:'A'}]} value={val} onChange={setVal} searchable />
452
467
  ```
453
468
 
469
+ **Select**:
470
+ - **Description:** **Form-specific** select component with label, error, hint, and validation support. Specifically designed for use in forms with proper semantics, accessibility, and validation integration. Use this component when building forms. For general UI selections (navigation, filters, etc.), use the **Dropdown** component instead. Based on Dropdown but includes form-specific features like `required` prop, hint text, and better integration with form libraries (React Hook Form, Formik, etc.).
471
+ - **Props:**
472
+ - **`options`**: `{ value: string; label: string; icon?: ReactNode; disabled?: boolean }[]` — required. Array of selectable options with optional icons and disabled state.
473
+ - **`value`**: `string` — optional. Currently selected value.
474
+ - **`onChange`**: `(value: string) => void` — required. Callback fired when selection changes.
475
+ - **`placeholder`**: `string` — default: `'Select an option'`. Shown when no value is selected.
476
+ - **`label`**: `string` — optional. Label displayed above the select.
477
+ - **`error`**: `string` — optional. Error message displayed below select with error styling.
478
+ - **`hint`**: `string` — optional. Helper text displayed below select when no error is present.
479
+ - **`searchable`**: `boolean` — default: `false`. Enables search input to filter options.
480
+ - **`clearable`**: `boolean` — default: `true`. Shows clear button when value is selected.
481
+ - **`required`**: `boolean` — optional. Displays asterisk (*) next to label and sets aria-required.
482
+ - **`disabled`**: `boolean` — optional. Disables the select.
483
+ - **`className`**: `string` — optional. Additional CSS classes for the wrapper.
484
+ - **Features:**
485
+ - **Form Integration:** Works seamlessly with form libraries (React Hook Form, Formik, etc.)
486
+ - **Validation Support:** Built-in error and hint display with animated transitions
487
+ - **Accessibility:** Full ARIA attributes, keyboard navigation, and screen reader support
488
+ - **Search:** Optional searchable mode with real-time filtering
489
+ - **Icons:** Support for icons in options for better visual recognition
490
+ - **Disabled Options:** Individual options can be disabled while keeping select active
491
+ - **Clearable:** Optional clear button to reset selection
492
+ - **Touch-Friendly:** Optimized for mobile with proper touch targets
493
+ - **Usage:**
494
+
495
+ ```jsx
496
+ import { Select } from '@stackloop/ui'
497
+ import { User, Settings, Bell } from 'lucide-react'
498
+
499
+ // Basic usage
500
+ <Select
501
+ label="Country"
502
+ options={[
503
+ { value: 'us', label: 'United States' },
504
+ { value: 'uk', label: 'United Kingdom' },
505
+ { value: 'ca', label: 'Canada' }
506
+ ]}
507
+ value={country}
508
+ onChange={setCountry}
509
+ required
510
+ />
511
+
512
+ // With icons and search
513
+ <Select
514
+ label="Navigation"
515
+ options={[
516
+ { value: 'profile', label: 'Profile', icon: <User /> },
517
+ { value: 'settings', label: 'Settings', icon: <Settings /> },
518
+ { value: 'notifications', label: 'Notifications', icon: <Bell />, disabled: true }
519
+ ]}
520
+ value={selected}
521
+ onChange={setSelected}
522
+ searchable
523
+ placeholder="Choose a page"
524
+ />
525
+
526
+ // With error and hint
527
+ <Select
528
+ label="Payment Method"
529
+ options={paymentMethods}
530
+ value={payment}
531
+ onChange={setPayment}
532
+ error={errors.payment}
533
+ hint="Select your preferred payment method"
534
+ required
535
+ />
536
+
537
+ // With React Hook Form
538
+ import { useForm, Controller } from 'react-hook-form'
539
+
540
+ const { control, formState: { errors } } = useForm()
541
+
542
+ <Controller
543
+ name="category"
544
+ control={control}
545
+ rules={{ required: 'Category is required' }}
546
+ render={({ field }) => (
547
+ <Select
548
+ label="Category"
549
+ options={categories}
550
+ value={field.value}
551
+ onChange={field.onChange}
552
+ error={errors.category?.message}
553
+ required
554
+ />
555
+ )}
556
+ />
557
+ ```
558
+
454
559
  **BottomSheet**:
455
560
  - **Description:** Mobile bottom sheet with header and optional close button.
456
561
  - **Props:**
@@ -528,6 +633,129 @@ You can add dark mode variants:
528
633
  <Badge variant="primary">New</Badge>
529
634
  ```
530
635
 
636
+ **Spinner**:
637
+ - **Description:** Animated loading spinner with size variants and optional label.
638
+ - **Props:**
639
+ - **`size`**: `'sm' | 'md' | 'lg' | 'xl'` — default: `'md'`. Controls spinner dimensions (sm=16px, md=32px, lg=48px, xl=64px).
640
+ - **`variant`**: `'primary' | 'secondary' | 'white'` — default: `'primary'`. Color variant of the spinner.
641
+ - **`label`**: `string` — optional. Text displayed below spinner.
642
+ - **`className`**: `string` — optional. Additional CSS classes for the wrapper.
643
+ - **Usage:**
644
+
645
+ ```jsx
646
+ import { Spinner } from '@stackloop/ui'
647
+
648
+ // Basic spinner
649
+ <Spinner />
650
+
651
+ // With label
652
+ <Spinner label="Loading..." />
653
+
654
+ // Custom size and variant
655
+ <Spinner size="lg" variant="white" />
656
+
657
+ // In a button
658
+ <Button disabled>
659
+ <Spinner size="sm" variant="white" label="Processing..." />
660
+ </Button>
661
+ ```
662
+
663
+ **Toast**:
664
+ - **Description:** Context-based toast notification system with variants, actions, and customizable positioning. Provides non-intrusive feedback messages that auto-dismiss.
665
+ - **Components:**
666
+ - **`ToastProvider`**: Wrapper component that manages toast state and rendering. Must wrap your app or components that use toasts.
667
+ - **`useToast`**: Hook to trigger toasts from any component within the provider.
668
+ - **ToastProvider Props:**
669
+ - **`children`**: `ReactNode` — required. Your app content.
670
+ - **`position`**: `'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right'` — default: `'top-right'`. Where toasts appear on screen.
671
+ - **`maxToasts`**: `number` — default: `5`. Maximum number of toasts shown at once.
672
+ - **useToast() Returns:**
673
+ - **`addToast(toast)`**: Function to create a new toast notification.
674
+ - **`removeToast(id)`**: Function to manually dismiss a toast.
675
+ - **`toasts`**: Array of currently active toasts.
676
+ - **Toast Object:**
677
+ - **`message`**: `string` — required. Toast content text.
678
+ - **`variant`**: `'success' | 'error' | 'warning' | 'info' | 'default'` — optional (default: `'default'`). Visual style with corresponding icon.
679
+ - **`duration`**: `number` — optional (default: `5000`ms). Auto-dismiss time in milliseconds. Set to `0` for persistent toast.
680
+ - **`action`**: `{ label: string; onClick: () => void }` — optional. Action button within the toast.
681
+ - **Features:**
682
+ - **Auto-dismiss**: Toasts automatically disappear after duration
683
+ - **Manual dismiss**: Click X button or call `removeToast(id)`
684
+ - **Variants with icons**: Visual feedback with CheckCircle, AlertCircle, AlertTriangle, Info icons
685
+ - **Action buttons**: Add clickable actions to toasts
686
+ - **Animations**: Smooth enter/exit animations with Framer Motion
687
+ - **Position control**: Place toasts anywhere on screen
688
+ - **Max limit**: Prevents toast overflow
689
+ - **Responsive**: Adapts to mobile screens
690
+ - **Usage:**
691
+
692
+ ```jsx
693
+ import { ToastProvider, useToast } from '@stackloop/ui'
694
+
695
+ // 1. Wrap your app with ToastProvider
696
+ function App() {
697
+ return (
698
+ <ToastProvider position="top-right" maxToasts={5}>
699
+ <YourApp />
700
+ </ToastProvider>
701
+ )
702
+ }
703
+
704
+ // 2. Use toasts in any component
705
+ function MyComponent() {
706
+ const { addToast } = useToast()
707
+
708
+ const handleSuccess = () => {
709
+ addToast({
710
+ message: 'Profile updated successfully!',
711
+ variant: 'success',
712
+ duration: 3000
713
+ })
714
+ }
715
+
716
+ const handleError = () => {
717
+ addToast({
718
+ message: 'Failed to save changes',
719
+ variant: 'error',
720
+ duration: 5000
721
+ })
722
+ }
723
+
724
+ const handleWithAction = () => {
725
+ addToast({
726
+ message: 'New message received',
727
+ variant: 'info',
728
+ duration: 0, // Persistent until dismissed
729
+ action: {
730
+ label: 'View',
731
+ onClick: () => navigate('/messages')
732
+ }
733
+ })
734
+ }
735
+
736
+ const handleWarning = () => {
737
+ addToast({
738
+ message: 'Your session will expire in 5 minutes',
739
+ variant: 'warning'
740
+ })
741
+ }
742
+
743
+ return (
744
+ <div>
745
+ <Button onClick={handleSuccess}>Save</Button>
746
+ <Button onClick={handleError}>Trigger Error</Button>
747
+ <Button onClick={handleWithAction}>Show Notification</Button>
748
+ <Button onClick={handleWarning}>Show Warning</Button>
749
+ </div>
750
+ )
751
+ }
752
+
753
+ // 3. Bottom-centered positioning
754
+ <ToastProvider position="bottom-center">
755
+ <App />
756
+ </ToastProvider>
757
+ ```
758
+
531
759
  **FloatingActionButton (FAB)**:
532
760
  - **Description:** Floating action button with expanded action list support.
533
761
  - **Props:**
@@ -0,0 +1,20 @@
1
+ import React from 'react';
2
+ export interface SelectOption {
3
+ value: string;
4
+ label: string;
5
+ icon?: React.ReactNode;
6
+ disabled?: boolean;
7
+ }
8
+ export interface SelectProps extends Omit<React.SelectHTMLAttributes<HTMLSelectElement>, 'onChange' | 'size'> {
9
+ options: SelectOption[];
10
+ value?: string;
11
+ onChange: (value: string) => void;
12
+ placeholder?: string;
13
+ label?: string;
14
+ error?: string;
15
+ hint?: string;
16
+ searchable?: boolean;
17
+ clearable?: boolean;
18
+ className?: string;
19
+ }
20
+ export declare const Select: React.ForwardRefExoticComponent<SelectProps & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ export interface SpinnerProps {
3
+ size?: 'sm' | 'md' | 'lg' | 'xl';
4
+ variant?: 'primary' | 'secondary' | 'white';
5
+ className?: string;
6
+ label?: string;
7
+ }
8
+ export declare const Spinner: React.FC<SpinnerProps>;
@@ -0,0 +1,26 @@
1
+ import React from 'react';
2
+ export type ToastVariant = 'success' | 'error' | 'warning' | 'info' | 'default';
3
+ export type ToastPosition = 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right';
4
+ export interface Toast {
5
+ id: string;
6
+ message: string;
7
+ variant?: ToastVariant;
8
+ duration?: number;
9
+ action?: {
10
+ label: string;
11
+ onClick: () => void;
12
+ };
13
+ }
14
+ interface ToastContextType {
15
+ toasts: Toast[];
16
+ addToast: (toast: Omit<Toast, 'id'>) => void;
17
+ removeToast: (id: string) => void;
18
+ }
19
+ export declare const useToast: () => ToastContextType;
20
+ interface ToastProviderProps {
21
+ children: React.ReactNode;
22
+ position?: ToastPosition;
23
+ maxToasts?: number;
24
+ }
25
+ export declare const ToastProvider: React.FC<ToastProviderProps>;
26
+ export {};