@stackloop/ui 1.0.9 → 2.0.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 +213 -0
- 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 +1111 -830
- package/dist/index.js.map +1 -1
- package/dist/ui.css +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -451,6 +451,96 @@ You can add dark mode variants:
|
|
|
451
451
|
<Dropdown options={[{value:'a',label:'A'}]} value={val} onChange={setVal} searchable />
|
|
452
452
|
```
|
|
453
453
|
|
|
454
|
+
**Select**:
|
|
455
|
+
- **Description:** Form-optimized select component with label, error, hint, and validation support. Built for forms with proper semantics and accessibility. Based on Dropdown but includes form-specific features like `required` prop, hint text, and better integration with form libraries.
|
|
456
|
+
- **Props:**
|
|
457
|
+
- **`options`**: `{ value: string; label: string; icon?: ReactNode; disabled?: boolean }[]` — required. Array of selectable options with optional icons and disabled state.
|
|
458
|
+
- **`value`**: `string` — optional. Currently selected value.
|
|
459
|
+
- **`onChange`**: `(value: string) => void` — required. Callback fired when selection changes.
|
|
460
|
+
- **`placeholder`**: `string` — default: `'Select an option'`. Shown when no value is selected.
|
|
461
|
+
- **`label`**: `string` — optional. Label displayed above the select.
|
|
462
|
+
- **`error`**: `string` — optional. Error message displayed below select with error styling.
|
|
463
|
+
- **`hint`**: `string` — optional. Helper text displayed below select when no error is present.
|
|
464
|
+
- **`searchable`**: `boolean` — default: `false`. Enables search input to filter options.
|
|
465
|
+
- **`clearable`**: `boolean` — default: `true`. Shows clear button when value is selected.
|
|
466
|
+
- **`required`**: `boolean` — optional. Displays asterisk (*) next to label and sets aria-required.
|
|
467
|
+
- **`disabled`**: `boolean` — optional. Disables the select.
|
|
468
|
+
- **`className`**: `string` — optional. Additional CSS classes for the wrapper.
|
|
469
|
+
- **Features:**
|
|
470
|
+
- **Form Integration:** Works seamlessly with form libraries (React Hook Form, Formik, etc.)
|
|
471
|
+
- **Validation Support:** Built-in error and hint display with animated transitions
|
|
472
|
+
- **Accessibility:** Full ARIA attributes, keyboard navigation, and screen reader support
|
|
473
|
+
- **Search:** Optional searchable mode with real-time filtering
|
|
474
|
+
- **Icons:** Support for icons in options for better visual recognition
|
|
475
|
+
- **Disabled Options:** Individual options can be disabled while keeping select active
|
|
476
|
+
- **Clearable:** Optional clear button to reset selection
|
|
477
|
+
- **Touch-Friendly:** Optimized for mobile with proper touch targets
|
|
478
|
+
- **Usage:**
|
|
479
|
+
|
|
480
|
+
```jsx
|
|
481
|
+
import { Select } from '@stackloop/ui'
|
|
482
|
+
import { User, Settings, Bell } from 'lucide-react'
|
|
483
|
+
|
|
484
|
+
// Basic usage
|
|
485
|
+
<Select
|
|
486
|
+
label="Country"
|
|
487
|
+
options={[
|
|
488
|
+
{ value: 'us', label: 'United States' },
|
|
489
|
+
{ value: 'uk', label: 'United Kingdom' },
|
|
490
|
+
{ value: 'ca', label: 'Canada' }
|
|
491
|
+
]}
|
|
492
|
+
value={country}
|
|
493
|
+
onChange={setCountry}
|
|
494
|
+
required
|
|
495
|
+
/>
|
|
496
|
+
|
|
497
|
+
// With icons and search
|
|
498
|
+
<Select
|
|
499
|
+
label="Navigation"
|
|
500
|
+
options={[
|
|
501
|
+
{ value: 'profile', label: 'Profile', icon: <User /> },
|
|
502
|
+
{ value: 'settings', label: 'Settings', icon: <Settings /> },
|
|
503
|
+
{ value: 'notifications', label: 'Notifications', icon: <Bell />, disabled: true }
|
|
504
|
+
]}
|
|
505
|
+
value={selected}
|
|
506
|
+
onChange={setSelected}
|
|
507
|
+
searchable
|
|
508
|
+
placeholder="Choose a page"
|
|
509
|
+
/>
|
|
510
|
+
|
|
511
|
+
// With error and hint
|
|
512
|
+
<Select
|
|
513
|
+
label="Payment Method"
|
|
514
|
+
options={paymentMethods}
|
|
515
|
+
value={payment}
|
|
516
|
+
onChange={setPayment}
|
|
517
|
+
error={errors.payment}
|
|
518
|
+
hint="Select your preferred payment method"
|
|
519
|
+
required
|
|
520
|
+
/>
|
|
521
|
+
|
|
522
|
+
// With React Hook Form
|
|
523
|
+
import { useForm, Controller } from 'react-hook-form'
|
|
524
|
+
|
|
525
|
+
const { control, formState: { errors } } = useForm()
|
|
526
|
+
|
|
527
|
+
<Controller
|
|
528
|
+
name="category"
|
|
529
|
+
control={control}
|
|
530
|
+
rules={{ required: 'Category is required' }}
|
|
531
|
+
render={({ field }) => (
|
|
532
|
+
<Select
|
|
533
|
+
label="Category"
|
|
534
|
+
options={categories}
|
|
535
|
+
value={field.value}
|
|
536
|
+
onChange={field.onChange}
|
|
537
|
+
error={errors.category?.message}
|
|
538
|
+
required
|
|
539
|
+
/>
|
|
540
|
+
)}
|
|
541
|
+
/>
|
|
542
|
+
```
|
|
543
|
+
|
|
454
544
|
**BottomSheet**:
|
|
455
545
|
- **Description:** Mobile bottom sheet with header and optional close button.
|
|
456
546
|
- **Props:**
|
|
@@ -528,6 +618,129 @@ You can add dark mode variants:
|
|
|
528
618
|
<Badge variant="primary">New</Badge>
|
|
529
619
|
```
|
|
530
620
|
|
|
621
|
+
**Spinner**:
|
|
622
|
+
- **Description:** Animated loading spinner with size variants and optional label.
|
|
623
|
+
- **Props:**
|
|
624
|
+
- **`size`**: `'sm' | 'md' | 'lg' | 'xl'` — default: `'md'`. Controls spinner dimensions (sm=16px, md=32px, lg=48px, xl=64px).
|
|
625
|
+
- **`variant`**: `'primary' | 'secondary' | 'white'` — default: `'primary'`. Color variant of the spinner.
|
|
626
|
+
- **`label`**: `string` — optional. Text displayed below spinner.
|
|
627
|
+
- **`className`**: `string` — optional. Additional CSS classes for the wrapper.
|
|
628
|
+
- **Usage:**
|
|
629
|
+
|
|
630
|
+
```jsx
|
|
631
|
+
import { Spinner } from '@stackloop/ui'
|
|
632
|
+
|
|
633
|
+
// Basic spinner
|
|
634
|
+
<Spinner />
|
|
635
|
+
|
|
636
|
+
// With label
|
|
637
|
+
<Spinner label="Loading..." />
|
|
638
|
+
|
|
639
|
+
// Custom size and variant
|
|
640
|
+
<Spinner size="lg" variant="white" />
|
|
641
|
+
|
|
642
|
+
// In a button
|
|
643
|
+
<Button disabled>
|
|
644
|
+
<Spinner size="sm" variant="white" label="Processing..." />
|
|
645
|
+
</Button>
|
|
646
|
+
```
|
|
647
|
+
|
|
648
|
+
**Toast**:
|
|
649
|
+
- **Description:** Context-based toast notification system with variants, actions, and customizable positioning. Provides non-intrusive feedback messages that auto-dismiss.
|
|
650
|
+
- **Components:**
|
|
651
|
+
- **`ToastProvider`**: Wrapper component that manages toast state and rendering. Must wrap your app or components that use toasts.
|
|
652
|
+
- **`useToast`**: Hook to trigger toasts from any component within the provider.
|
|
653
|
+
- **ToastProvider Props:**
|
|
654
|
+
- **`children`**: `ReactNode` — required. Your app content.
|
|
655
|
+
- **`position`**: `'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right'` — default: `'top-right'`. Where toasts appear on screen.
|
|
656
|
+
- **`maxToasts`**: `number` — default: `5`. Maximum number of toasts shown at once.
|
|
657
|
+
- **useToast() Returns:**
|
|
658
|
+
- **`addToast(toast)`**: Function to create a new toast notification.
|
|
659
|
+
- **`removeToast(id)`**: Function to manually dismiss a toast.
|
|
660
|
+
- **`toasts`**: Array of currently active toasts.
|
|
661
|
+
- **Toast Object:**
|
|
662
|
+
- **`message`**: `string` — required. Toast content text.
|
|
663
|
+
- **`variant`**: `'success' | 'error' | 'warning' | 'info' | 'default'` — optional (default: `'default'`). Visual style with corresponding icon.
|
|
664
|
+
- **`duration`**: `number` — optional (default: `5000`ms). Auto-dismiss time in milliseconds. Set to `0` for persistent toast.
|
|
665
|
+
- **`action`**: `{ label: string; onClick: () => void }` — optional. Action button within the toast.
|
|
666
|
+
- **Features:**
|
|
667
|
+
- **Auto-dismiss**: Toasts automatically disappear after duration
|
|
668
|
+
- **Manual dismiss**: Click X button or call `removeToast(id)`
|
|
669
|
+
- **Variants with icons**: Visual feedback with CheckCircle, AlertCircle, AlertTriangle, Info icons
|
|
670
|
+
- **Action buttons**: Add clickable actions to toasts
|
|
671
|
+
- **Animations**: Smooth enter/exit animations with Framer Motion
|
|
672
|
+
- **Position control**: Place toasts anywhere on screen
|
|
673
|
+
- **Max limit**: Prevents toast overflow
|
|
674
|
+
- **Responsive**: Adapts to mobile screens
|
|
675
|
+
- **Usage:**
|
|
676
|
+
|
|
677
|
+
```jsx
|
|
678
|
+
import { ToastProvider, useToast } from '@stackloop/ui'
|
|
679
|
+
|
|
680
|
+
// 1. Wrap your app with ToastProvider
|
|
681
|
+
function App() {
|
|
682
|
+
return (
|
|
683
|
+
<ToastProvider position="top-right" maxToasts={5}>
|
|
684
|
+
<YourApp />
|
|
685
|
+
</ToastProvider>
|
|
686
|
+
)
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
// 2. Use toasts in any component
|
|
690
|
+
function MyComponent() {
|
|
691
|
+
const { addToast } = useToast()
|
|
692
|
+
|
|
693
|
+
const handleSuccess = () => {
|
|
694
|
+
addToast({
|
|
695
|
+
message: 'Profile updated successfully!',
|
|
696
|
+
variant: 'success',
|
|
697
|
+
duration: 3000
|
|
698
|
+
})
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
const handleError = () => {
|
|
702
|
+
addToast({
|
|
703
|
+
message: 'Failed to save changes',
|
|
704
|
+
variant: 'error',
|
|
705
|
+
duration: 5000
|
|
706
|
+
})
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
const handleWithAction = () => {
|
|
710
|
+
addToast({
|
|
711
|
+
message: 'New message received',
|
|
712
|
+
variant: 'info',
|
|
713
|
+
duration: 0, // Persistent until dismissed
|
|
714
|
+
action: {
|
|
715
|
+
label: 'View',
|
|
716
|
+
onClick: () => navigate('/messages')
|
|
717
|
+
}
|
|
718
|
+
})
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
const handleWarning = () => {
|
|
722
|
+
addToast({
|
|
723
|
+
message: 'Your session will expire in 5 minutes',
|
|
724
|
+
variant: 'warning'
|
|
725
|
+
})
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
return (
|
|
729
|
+
<div>
|
|
730
|
+
<Button onClick={handleSuccess}>Save</Button>
|
|
731
|
+
<Button onClick={handleError}>Trigger Error</Button>
|
|
732
|
+
<Button onClick={handleWithAction}>Show Notification</Button>
|
|
733
|
+
<Button onClick={handleWarning}>Show Warning</Button>
|
|
734
|
+
</div>
|
|
735
|
+
)
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
// 3. Bottom-centered positioning
|
|
739
|
+
<ToastProvider position="bottom-center">
|
|
740
|
+
<App />
|
|
741
|
+
</ToastProvider>
|
|
742
|
+
```
|
|
743
|
+
|
|
531
744
|
**FloatingActionButton (FAB)**:
|
|
532
745
|
- **Description:** Floating action button with expanded action list support.
|
|
533
746
|
- **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 {};
|