@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 +230 -2
- package/dist/Select.d.ts +20 -0
- package/dist/Spinner.d.ts +8 -0
- package/dist/Toast.d.ts +26 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.js +1036 -739
- package/dist/index.js.map +1 -1
- package/dist/ui.css +1 -1
- package/package.json +2 -2
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:**
|
|
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:**
|
package/dist/Select.d.ts
ADDED
|
@@ -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>>;
|
package/dist/Toast.d.ts
ADDED
|
@@ -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 {};
|