@stackloop/ui 3.3.5 → 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 +174 -6
- package/dist/FloatingPortal.d.ts +12 -0
- package/dist/Input.d.ts +7 -1
- package/dist/MultiSelect.d.ts +23 -0
- package/dist/PhoneInput.d.ts +1 -1
- package/dist/Toast.d.ts +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3399 -2833
- package/dist/index.js.map +1 -1
- package/dist/ripple.d.ts +6 -0
- package/dist/stackloop-ui.css +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -163,7 +163,34 @@ 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
|
+
|
|
168
|
+
## Ripple Behavior
|
|
169
|
+
|
|
170
|
+
Ripple effects are **opt-in**. Initialize them once in your app entry file.
|
|
171
|
+
|
|
172
|
+
```tsx
|
|
173
|
+
// main.tsx or index.tsx
|
|
174
|
+
import { setupRippleEffects } from '@stackloop/ui'
|
|
175
|
+
|
|
176
|
+
setupRippleEffects()
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
After setup, ripple can apply to interactive elements (`button`, `a`, `[role="button"]`, and elements with `data-ripple="true"`).
|
|
180
|
+
|
|
181
|
+
- Disable ripple for a specific element:
|
|
182
|
+
|
|
183
|
+
```html
|
|
184
|
+
<button data-ripple="false">No ripple</button>
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
- Force ripple on a custom/non-button element:
|
|
188
|
+
|
|
189
|
+
```html
|
|
190
|
+
<div role="button" data-ripple="true">Ripple enabled</div>
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Call `setupRippleEffects()` only once per app (for example in `main.tsx`) to avoid duplicate global listeners.
|
|
167
194
|
|
|
168
195
|
**Checkbox**:
|
|
169
196
|
- **Description:** Accessible checkbox with optional label and description.
|
|
@@ -199,16 +226,25 @@ Components with the `animate` prop:
|
|
|
199
226
|
```
|
|
200
227
|
|
|
201
228
|
**Input**:
|
|
202
|
-
- **Description:**
|
|
229
|
+
- **Description:** Unified input API with smart type routing. Supports native text/password/email/etc plus `phone`, `country`, and `date` while keeping a consistent `value` + `onChange` pattern.
|
|
203
230
|
- **Props:**
|
|
231
|
+
- **`type`**: Native HTML input types plus `'phone' | 'country'`.
|
|
232
|
+
- `type="date"` renders the library `DatePicker`.
|
|
233
|
+
- `type="country"` renders `CountrySelect`.
|
|
234
|
+
- `type="phone"` renders `PhoneInput`.
|
|
235
|
+
- **`value`**: `string | number | readonly string[] | Date`.
|
|
236
|
+
- **`onChange`**: `(value: string | Date) => void` — value-based callback used consistently across smart/native modes.
|
|
237
|
+
- **`onValueChange`**: `(value: string | Date) => void` — optional backward-compatible alias.
|
|
204
238
|
- **`label`**: `string` — optional.
|
|
205
239
|
- **`error`**: `string` — optional.
|
|
206
240
|
- **`hint`**: `string` — optional.
|
|
207
241
|
- **`leftIcon`** / **`rightIcon`**: `ReactNode` — optional.
|
|
208
242
|
- **`className`**: `string` — optional.
|
|
209
|
-
- Inherits `input` HTML attributes.
|
|
243
|
+
- Inherits most `input` HTML attributes.
|
|
210
244
|
- **Features:**
|
|
211
245
|
- **Password Toggle:** When `type="password"`, automatically displays an Eye/EyeOff icon to toggle password visibility. This overrides any custom `rightIcon`.
|
|
246
|
+
- **Consistent Value API:** Same controlled pattern for text, phone, country, and date.
|
|
247
|
+
- **Lightweight Composition:** Smart types reuse existing specialized components instead of duplicating logic.
|
|
212
248
|
- **Icon Support:** Display icons on left or right side of input
|
|
213
249
|
- **Validation:** Built-in error and hint display
|
|
214
250
|
- **Accessibility:** Proper labels and ARIA attributes
|
|
@@ -221,6 +257,32 @@ Components with the `animate` prop:
|
|
|
221
257
|
|
|
222
258
|
// Password with automatic toggle
|
|
223
259
|
<Input label="Password" type="password" placeholder="Enter password" />
|
|
260
|
+
|
|
261
|
+
// Phone (single combined value)
|
|
262
|
+
<Input
|
|
263
|
+
label="Phone"
|
|
264
|
+
type="phone"
|
|
265
|
+
value={phone}
|
|
266
|
+
onChange={(nextValue) => setPhone(String(nextValue))}
|
|
267
|
+
/>
|
|
268
|
+
|
|
269
|
+
// Country
|
|
270
|
+
<Input
|
|
271
|
+
label="Country"
|
|
272
|
+
type="country"
|
|
273
|
+
value={country}
|
|
274
|
+
onChange={(nextValue) => setCountry(String(nextValue))}
|
|
275
|
+
/>
|
|
276
|
+
|
|
277
|
+
// Date
|
|
278
|
+
<Input
|
|
279
|
+
label="Date"
|
|
280
|
+
type="date"
|
|
281
|
+
value={selectedDate}
|
|
282
|
+
onChange={(nextValue) => {
|
|
283
|
+
if (nextValue instanceof Date) setSelectedDate(nextValue)
|
|
284
|
+
}}
|
|
285
|
+
/>
|
|
224
286
|
|
|
225
287
|
// With icons
|
|
226
288
|
<Input
|
|
@@ -231,10 +293,10 @@ Components with the `animate` prop:
|
|
|
231
293
|
```
|
|
232
294
|
|
|
233
295
|
**PhoneInput**:
|
|
234
|
-
- **Description:** Phone
|
|
296
|
+
- **Description:** Phone input with country selector and a single editable value field (country code + number in the same input).
|
|
235
297
|
- **Props:**
|
|
236
298
|
- **`value`**: `string` — optional.
|
|
237
|
-
- **`onChange`**: `(
|
|
299
|
+
- **`onChange`**: `(value: string) => void` — optional.
|
|
238
300
|
- **`country`**: `string` — optional ISO2 override (e.g. `US`).
|
|
239
301
|
- **`defaultCountry`**: `string` — default: `'US'`.
|
|
240
302
|
- **`autoDetect`**: `boolean` — default: `true`. Uses `navigator.language` to select a country.
|
|
@@ -248,6 +310,10 @@ Components with the `animate` prop:
|
|
|
248
310
|
- **`animate`**: `boolean` — default: `true`.
|
|
249
311
|
- **`className`**: `string` — optional.
|
|
250
312
|
- Inherits standard `input` HTML attributes.
|
|
313
|
+
- **Behavior:**
|
|
314
|
+
- Default selected country auto-fills dial code when the input is empty.
|
|
315
|
+
- User can edit/delete the dial code directly in the same input.
|
|
316
|
+
- Dropdown opens up or down based on available viewport space.
|
|
251
317
|
- **Usage:**
|
|
252
318
|
|
|
253
319
|
```jsx
|
|
@@ -256,7 +322,7 @@ Components with the `animate` prop:
|
|
|
256
322
|
<PhoneInput
|
|
257
323
|
label="Phone"
|
|
258
324
|
value={phone}
|
|
259
|
-
onChange={
|
|
325
|
+
onChange={setPhone}
|
|
260
326
|
required
|
|
261
327
|
hint="Include area code"
|
|
262
328
|
searchable
|
|
@@ -640,6 +706,108 @@ Components with the `animate` prop:
|
|
|
640
706
|
/>
|
|
641
707
|
```
|
|
642
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
|
+
|
|
643
811
|
**BottomSheet**:
|
|
644
812
|
- **Description:** Mobile bottom sheet with header and optional close button.
|
|
645
813
|
- **Props:**
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
interface FloatingPortalProps {
|
|
3
|
+
open: boolean;
|
|
4
|
+
anchorRef: React.RefObject<HTMLElement | null>;
|
|
5
|
+
placement: 'top' | 'bottom';
|
|
6
|
+
children: React.ReactNode;
|
|
7
|
+
className?: string;
|
|
8
|
+
offset?: number;
|
|
9
|
+
matchWidth?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare const FloatingPortal: React.FC<FloatingPortalProps>;
|
|
12
|
+
export {};
|
package/dist/Input.d.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
|
|
2
|
+
type SmartInputType = React.HTMLInputTypeAttribute | 'country' | 'phone';
|
|
3
|
+
export interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'type' | 'value' | 'onChange'> {
|
|
4
|
+
type?: SmartInputType;
|
|
5
|
+
value?: string | number | readonly string[] | Date;
|
|
6
|
+
onChange?: (value: string | Date) => void;
|
|
7
|
+
onValueChange?: (value: string | Date) => void;
|
|
3
8
|
label?: string;
|
|
4
9
|
error?: string;
|
|
5
10
|
hint?: string;
|
|
@@ -8,3 +13,4 @@ export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement>
|
|
|
8
13
|
animate?: boolean;
|
|
9
14
|
}
|
|
10
15
|
export declare const Input: React.ForwardRefExoticComponent<InputProps & React.RefAttributes<HTMLInputElement>>;
|
|
16
|
+
export {};
|
|
@@ -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>>;
|
package/dist/PhoneInput.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { type Country } from './countries';
|
|
3
3
|
export interface PhoneInputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange'> {
|
|
4
4
|
value?: string;
|
|
5
|
-
onChange?: (
|
|
5
|
+
onChange?: (value: string) => void;
|
|
6
6
|
country?: string;
|
|
7
7
|
defaultCountry?: string;
|
|
8
8
|
autoDetect?: boolean;
|
package/dist/Toast.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
export type ToastVariant = 'success' | 'error' | 'warning' | 'info' | 'default';
|
|
2
|
+
export type ToastVariant = 'success' | 'error' | 'warning' | 'info' | 'default' | 'loading';
|
|
3
3
|
export type ToastPosition = 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right';
|
|
4
4
|
export interface Toast {
|
|
5
5
|
id: string;
|