@stackloop/ui 4.0.3 → 4.0.4

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
@@ -163,7 +163,7 @@ import { Button, Modal } from '@stackloop/ui'
163
163
 
164
164
  Components with the `animate` prop:
165
165
 
166
- - `AudioRecorder`, `Badge`, `BottomSheet`, `Button`, `Card`, `CameraCapture`, `Checkbox`, `CountrySelect`, `DatePicker`, `Drawer`, `Dropdown`, `DualSlider`, `FileUploader`, `FloatingActionButton`, `Input`, `Modal`, `Pagination`, `PhoneInput`, `RadioPills`, `Select`, `Slider`, `Spinner`, `StepProgress`, `Table`, `Textarea`, `ThumbnailGrid`, `Toggle`, `ToastProvider`
166
+ - `AudioRecorder`, `Badge`, `BottomSheet`, `Button`, `Card`, `CameraCapture`, `Checkbox`, `CountrySelect`, `DatePicker`, `Drawer`, `Dropdown`, `DualSlider`, `FileUploader`, `FloatingActionButton`, `Input`, `Modal`, `MultiSelect`, `Pagination`, `PhoneInput`, `RadioPills`, `Select`, `Slider`, `Spinner`, `StepProgress`, `Table`, `Textarea`, `ThumbnailGrid`, `Toggle`, `ToastProvider`
167
167
 
168
168
  ## Ripple Behavior
169
169
 
@@ -706,6 +706,108 @@ Call `setupRippleEffects()` only once per app (for example in `main.tsx`) to avo
706
706
  />
707
707
  ```
708
708
 
709
+ **MultiSelect**:
710
+ - **Description:** Multi-select component that allows selecting multiple items with chips display. Each selected item appears as a removable chip with an X button. Perfect for selecting multiple options like staff members, tags, skills, etc.
711
+ - **Props:**
712
+ - **`options`**: `{ value: string; label: string; icon?: ReactNode; disabled?: boolean }[]` — required. Array of selectable options with optional icons and disabled state.
713
+ - **`value`**: `string[]` — optional. Array of currently selected values (default: `[]`).
714
+ - **`onChange`**: `(values: string[]) => void` — required. Callback fired when selections change, receives array of selected values.
715
+ - **`placeholder`**: `string` — default: `'Select options...'`. Shown when no items are selected.
716
+ - **`label`**: `string` — optional. Label displayed above the multi-select.
717
+ - **`error`**: `string` — optional. Error message displayed below with error styling.
718
+ - **`hint`**: `string` — optional. Helper text displayed below when no error is present.
719
+ - **`searchable`**: `boolean` — default: `false`. Enables search input to filter options.
720
+ - **`clearable`**: `boolean` — default: `true`. Shows "Clear all" option in dropdown when items are selected.
721
+ - **`maxItems`**: `number` — optional. Maximum number of items that can be selected.
722
+ - **`chipVariant`**: `'default' | 'primary' | 'success' | 'warning' | 'danger' | 'info'` — default: `'primary'`. Styles for the selected item chips.
723
+ - **`required`**: `boolean` — optional. Displays asterisk (*) next to label and sets aria-required.
724
+ - **`disabled`**: `boolean` — optional. Disables the multi-select.
725
+ - **`className`**: `string` — optional. Additional CSS classes for the wrapper.
726
+ - **Features:**
727
+ - **Chips Display:** Selected items show as removable chips with X buttons inline in the button
728
+ - **Multi-Select:** Choose multiple items from a list with checkboxes in the dropdown
729
+ - **Form Integration:** Works seamlessly with form libraries (React Hook Form, Formik, etc.)
730
+ - **Validation Support:** Built-in error and hint display with animated transitions
731
+ - **Accessibility:** Full ARIA attributes, keyboard navigation, and screen reader support
732
+ - **Search:** Optional searchable mode with real-time filtering across all options
733
+ - **Icons:** Support for icons in options for better visual recognition
734
+ - **Disabled Options:** Individual options can be disabled
735
+ - **Max Items:** Optional limit on the number of items that can be selected
736
+ - **Clear All:** "Clear all" button in dropdown to remove all selected items at once
737
+ - **Remove Individual:** Click X on any chip to remove just that item
738
+ - **Touch-Friendly:** Optimized for mobile with proper touch targets
739
+ - **Usage:**
740
+
741
+ ```jsx
742
+ import { MultiSelect } from '@stackloop/ui'
743
+ import { Users, Award, Code } from 'lucide-react'
744
+
745
+ // Basic multi-select for team members
746
+ const [selectedStaff, setSelectedStaff] = useState<string[]>([])
747
+
748
+ <MultiSelect
749
+ label="Select Staff Members"
750
+ options={[
751
+ { value: 'john', label: 'John Doe' },
752
+ { value: 'jane', label: 'Jane Smith' },
753
+ { value: 'bob', label: 'Bob Johnson' }
754
+ ]}
755
+ value={selectedStaff}
756
+ onChange={setSelectedStaff}
757
+ placeholder="Choose staff members..."
758
+ required
759
+ />
760
+
761
+ // With icons and search
762
+ <MultiSelect
763
+ label="Skills"
764
+ options={[
765
+ { value: 'react', label: 'React', icon: <Code /> },
766
+ { value: 'vue', label: 'Vue.js', icon: <Code /> },
767
+ { value: 'angular', label: 'Angular', icon: <Code /> },
768
+ { value: 'node', label: 'Node.js', icon: <Code /> }
769
+ ]}
770
+ value={skills}
771
+ onChange={setSkills}
772
+ searchable
773
+ placeholder="Select your skills"
774
+ />
775
+
776
+ // With max items limit and chip styling
777
+ <MultiSelect
778
+ label="Team Members (Max 5)"
779
+ options={teamMembers}
780
+ value={selectedTeam}
781
+ onChange={setSelectedTeam}
782
+ maxItems={5}
783
+ chipVariant="primary"
784
+ error={errors.team}
785
+ hint="Select up to 5 team members for this project"
786
+ />
787
+
788
+ // With React Hook Form
789
+ import { useForm, Controller } from 'react-hook-form'
790
+
791
+ const { control, formState: { errors } } = useForm()
792
+
793
+ <Controller
794
+ name="assignees"
795
+ control={control}
796
+ rules={{ validate: (v) => v?.length > 0 || 'Please select at least one assignee' }}
797
+ render={({ field }) => (
798
+ <MultiSelect
799
+ label="Assign to"
800
+ options={users}
801
+ value={field.value || []}
802
+ onChange={field.onChange}
803
+ error={errors.assignees?.message}
804
+ searchable
805
+ required
806
+ />
807
+ )}
808
+ />
809
+ ```
810
+
709
811
  **BottomSheet**:
710
812
  - **Description:** Mobile bottom sheet with header and optional close button.
711
813
  - **Props:**
@@ -0,0 +1,23 @@
1
+ import React from 'react';
2
+ export interface MultiSelectOption {
3
+ value: string;
4
+ label: string;
5
+ icon?: React.ReactNode;
6
+ disabled?: boolean;
7
+ }
8
+ export interface MultiSelectProps extends Omit<React.SelectHTMLAttributes<HTMLSelectElement>, 'onChange' | 'size'> {
9
+ options: MultiSelectOption[];
10
+ value?: string[];
11
+ onChange: (values: string[]) => void;
12
+ placeholder?: string;
13
+ label?: string;
14
+ error?: string;
15
+ hint?: string;
16
+ searchable?: boolean;
17
+ clearable?: boolean;
18
+ maxItems?: number;
19
+ className?: string;
20
+ animate?: boolean;
21
+ chipVariant?: 'default' | 'primary' | 'success' | 'warning' | 'danger' | 'info';
22
+ }
23
+ export declare const MultiSelect: React.ForwardRefExoticComponent<MultiSelectProps & React.RefAttributes<HTMLDivElement>>;