@stackloop/ui 4.1.2 → 4.2.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 +105 -13
- package/dist/ButtonGroup.d.ts +9 -0
- package/dist/DropdownMenu.d.ts +52 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2445 -2101
- package/dist/index.js.map +1 -1
- package/dist/stackloop-ui.css +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -225,9 +225,18 @@ Call `setupRippleEffects()` only once per app (for example in `main.tsx`) to avo
|
|
|
225
225
|
**ButtonGroup**:
|
|
226
226
|
- **Description:** Segmented button group with a rounded outer container and separate buttons divided by borders.
|
|
227
227
|
- **Props:**
|
|
228
|
-
- **`options`**: `
|
|
228
|
+
- **`options`**: `ButtonGroupOption[]` — required.
|
|
229
|
+
- `value`: `string` — required unique option value.
|
|
230
|
+
- `label`: `string` — required fallback text label.
|
|
231
|
+
- `icon`: `ReactNode` — optional leading icon.
|
|
232
|
+
- `disabled`: `boolean` — optional per-option disabled state.
|
|
233
|
+
- `className`: `string` — optional per-option class override.
|
|
234
|
+
- `ariaLabel`: `string` — optional aria-label for accessibility.
|
|
235
|
+
- `render`: `(option, state) => ReactNode` — optional custom content renderer. If omitted, the default icon + label layout is used.
|
|
236
|
+
- `onClick`: `(option, event) => void` — optional per-option click callback.
|
|
229
237
|
- **`value`**: `string` — optional selected value.
|
|
230
238
|
- **`onChange`**: `(value: string) => void` — optional change callback.
|
|
239
|
+
- **`onOptionClick`**: `(option, index) => void` — optional group-level callback for any option click.
|
|
231
240
|
- **`size`**: `'sm' | 'md' | 'lg'` — default: `'md'`.
|
|
232
241
|
- **`disabled`**: `boolean` — default: `false`.
|
|
233
242
|
- **`className`**: `string` — optional.
|
|
@@ -248,6 +257,32 @@ Call `setupRippleEffects()` only once per app (for example in `main.tsx`) to avo
|
|
|
248
257
|
value={selectedRange}
|
|
249
258
|
onChange={setSelectedRange}
|
|
250
259
|
/>
|
|
260
|
+
|
|
261
|
+
// Custom render + per-option and group-level click hooks
|
|
262
|
+
const customOptions = [
|
|
263
|
+
{
|
|
264
|
+
label: 'Overview',
|
|
265
|
+
value: 'overview',
|
|
266
|
+
render: (option, state) => (
|
|
267
|
+
<span className="flex flex-col items-start leading-tight">
|
|
268
|
+
<span className="text-[10px] uppercase tracking-[0.2em] opacity-70">View</span>
|
|
269
|
+
<span className={state.selected ? 'font-semibold' : 'font-medium'}>{option.label}</span>
|
|
270
|
+
</span>
|
|
271
|
+
),
|
|
272
|
+
onClick: (option) => console.log('Option clicked:', option.value)
|
|
273
|
+
},
|
|
274
|
+
{ label: 'Details', value: 'details' },
|
|
275
|
+
{ label: 'Activity', value: 'activity' }
|
|
276
|
+
]
|
|
277
|
+
|
|
278
|
+
<ButtonGroup
|
|
279
|
+
options={customOptions}
|
|
280
|
+
value={selectedView}
|
|
281
|
+
onChange={setSelectedView}
|
|
282
|
+
onOptionClick={(option, index) => {
|
|
283
|
+
console.log('Group handler:', option.value, index)
|
|
284
|
+
}}
|
|
285
|
+
/>
|
|
251
286
|
```
|
|
252
287
|
|
|
253
288
|
**Input**:
|
|
@@ -652,20 +687,77 @@ Call `setupRippleEffects()` only once per app (for example in `main.tsx`) to avo
|
|
|
652
687
|
/>
|
|
653
688
|
```
|
|
654
689
|
|
|
655
|
-
**
|
|
656
|
-
- **Description:**
|
|
657
|
-
- **
|
|
658
|
-
-
|
|
659
|
-
-
|
|
660
|
-
-
|
|
661
|
-
|
|
662
|
-
-
|
|
690
|
+
**DropdownMenu**:
|
|
691
|
+
- **Description:** Headless-style dropdown menu primitive with custom trigger support, outside-click close behavior, and nested submenus.
|
|
692
|
+
- **Placement behavior:**
|
|
693
|
+
- Root menu opens **down or up** based on viewport space (or can be forced).
|
|
694
|
+
- Root menu opens **right or left** based on viewport space and will clamp inside the viewport to avoid horizontal overflow.
|
|
695
|
+
- Nested submenus open **right or left** based on side space (or can be forced).
|
|
696
|
+
- **Use case:**
|
|
697
|
+
- Nested navigation or action menus where each menu item can itself open another submenu.
|
|
698
|
+
- Context menus launched from custom triggers like icon buttons, cards, or toolbar actions.
|
|
699
|
+
- **Core Components:**
|
|
700
|
+
- **`DropdownMenu`**: Root wrapper with controlled/uncontrolled open state.
|
|
701
|
+
- **`DropdownMenuTrigger`**: Trigger button or custom trigger with `asChild`.
|
|
702
|
+
- **`DropdownMenuContent`**: Root menu panel.
|
|
703
|
+
- **`DropdownMenuItem`**: Clickable item (button or custom child with `asChild`).
|
|
704
|
+
- **`DropdownMenuSub`**: Nested menu wrapper.
|
|
705
|
+
- **`DropdownMenuSubTrigger`**: Trigger for nested menu.
|
|
706
|
+
- **`DropdownMenuSubContent`**: Nested panel.
|
|
707
|
+
- **Root Props (`DropdownMenu`):**
|
|
708
|
+
- **`open`**: `boolean` — optional controlled state.
|
|
709
|
+
- **`defaultOpen`**: `boolean` — default: `false`.
|
|
710
|
+
- **`onOpenChange`**: `(open: boolean) => void` — optional callback.
|
|
711
|
+
- **`placement`**: `'auto' | 'top' | 'bottom'` — default: `'auto'`.
|
|
712
|
+
- **`className`**: `string` — optional.
|
|
713
|
+
- **Content Props (`DropdownMenuContent`):**
|
|
714
|
+
- **`side`**: `'auto' | 'left' | 'right'` — default: `'auto'`. Controls root horizontal direction.
|
|
715
|
+
- **`align`**: `'start' | 'end'` — default: `'start'`. Used as fallback preference when `side="auto"`.
|
|
716
|
+
- **`sideOffset`**: `number` — default: `8`. Gap between trigger and content.
|
|
663
717
|
- **Usage:**
|
|
664
718
|
|
|
665
719
|
```jsx
|
|
666
|
-
import {
|
|
667
|
-
|
|
668
|
-
|
|
720
|
+
import {
|
|
721
|
+
DropdownMenu,
|
|
722
|
+
DropdownMenuTrigger,
|
|
723
|
+
DropdownMenuContent,
|
|
724
|
+
DropdownMenuItem,
|
|
725
|
+
DropdownMenuSub,
|
|
726
|
+
DropdownMenuSubTrigger,
|
|
727
|
+
DropdownMenuSubContent
|
|
728
|
+
} from '@stackloop/ui'
|
|
729
|
+
|
|
730
|
+
<DropdownMenu>
|
|
731
|
+
<DropdownMenuTrigger>Open menu</DropdownMenuTrigger>
|
|
732
|
+
<DropdownMenuContent side="auto">
|
|
733
|
+
<DropdownMenuItem asChild>
|
|
734
|
+
<a href="/dashboard">Dashboard</a>
|
|
735
|
+
</DropdownMenuItem>
|
|
736
|
+
|
|
737
|
+
<DropdownMenuSub>
|
|
738
|
+
<DropdownMenuSubTrigger>Products</DropdownMenuSubTrigger>
|
|
739
|
+
<DropdownMenuSubContent>
|
|
740
|
+
<DropdownMenuItem asChild>
|
|
741
|
+
<a href="/products/new">New Arrivals</a>
|
|
742
|
+
</DropdownMenuItem>
|
|
743
|
+
|
|
744
|
+
<DropdownMenuSub>
|
|
745
|
+
<DropdownMenuSubTrigger>Categories</DropdownMenuSubTrigger>
|
|
746
|
+
<DropdownMenuSubContent>
|
|
747
|
+
<DropdownMenuItem asChild>
|
|
748
|
+
<a href="/products/categories/tools">Tools</a>
|
|
749
|
+
</DropdownMenuItem>
|
|
750
|
+
<DropdownMenuItem asChild>
|
|
751
|
+
<a href="/products/categories/home">Home</a>
|
|
752
|
+
</DropdownMenuItem>
|
|
753
|
+
</DropdownMenuSubContent>
|
|
754
|
+
</DropdownMenuSub>
|
|
755
|
+
</DropdownMenuSubContent>
|
|
756
|
+
</DropdownMenuSub>
|
|
757
|
+
|
|
758
|
+
<DropdownMenuItem onClick={() => console.log('Signed out')}>Sign out</DropdownMenuItem>
|
|
759
|
+
</DropdownMenuContent>
|
|
760
|
+
</DropdownMenu>
|
|
669
761
|
```
|
|
670
762
|
|
|
671
763
|
**Tooltip**:
|
|
@@ -695,7 +787,7 @@ Call `setupRippleEffects()` only once per app (for example in `main.tsx`) to avo
|
|
|
695
787
|
```
|
|
696
788
|
|
|
697
789
|
**Select**:
|
|
698
|
-
- **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.
|
|
790
|
+
- **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. It includes form-focused features like `required`, hint text, and better integration with form libraries (React Hook Form, Formik, etc.).
|
|
699
791
|
- **Props:**
|
|
700
792
|
- **`options`**: `{ value: string; label: string; icon?: ReactNode; disabled?: boolean }[]` — required. Array of selectable options with optional icons and disabled state.
|
|
701
793
|
- **`value`**: `string` — optional. Currently selected value.
|
package/dist/ButtonGroup.d.ts
CHANGED
|
@@ -4,11 +4,20 @@ export interface ButtonGroupOption {
|
|
|
4
4
|
label: string;
|
|
5
5
|
icon?: React.ReactNode;
|
|
6
6
|
disabled?: boolean;
|
|
7
|
+
className?: string;
|
|
8
|
+
ariaLabel?: string;
|
|
9
|
+
render?: (option: ButtonGroupOption, state: {
|
|
10
|
+
selected: boolean;
|
|
11
|
+
disabled: boolean;
|
|
12
|
+
index: number;
|
|
13
|
+
}) => React.ReactNode;
|
|
14
|
+
onClick?: (option: ButtonGroupOption, event: React.MouseEvent<HTMLButtonElement>) => void;
|
|
7
15
|
}
|
|
8
16
|
export interface ButtonGroupProps {
|
|
9
17
|
options: ButtonGroupOption[];
|
|
10
18
|
value?: string;
|
|
11
19
|
onChange?: (value: string) => void;
|
|
20
|
+
onOptionClick?: (option: ButtonGroupOption, index: number) => void;
|
|
12
21
|
size?: 'sm' | 'md' | 'lg';
|
|
13
22
|
disabled?: boolean;
|
|
14
23
|
className?: string;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { type HTMLMotionProps } from 'framer-motion';
|
|
3
|
+
type RootVerticalPlacement = 'auto' | 'top' | 'bottom';
|
|
4
|
+
type RootHorizontalPlacement = 'auto' | 'left' | 'right';
|
|
5
|
+
type SubmenuHorizontalPlacement = 'auto' | 'left' | 'right';
|
|
6
|
+
export interface DropdownMenuProps {
|
|
7
|
+
children: React.ReactNode;
|
|
8
|
+
open?: boolean;
|
|
9
|
+
defaultOpen?: boolean;
|
|
10
|
+
onOpenChange?: (open: boolean) => void;
|
|
11
|
+
placement?: RootVerticalPlacement;
|
|
12
|
+
className?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare const DropdownMenu: React.FC<DropdownMenuProps>;
|
|
15
|
+
export interface DropdownMenuTriggerProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
16
|
+
asChild?: boolean;
|
|
17
|
+
children: React.ReactNode;
|
|
18
|
+
}
|
|
19
|
+
export declare const DropdownMenuTrigger: React.ForwardRefExoticComponent<DropdownMenuTriggerProps & React.RefAttributes<HTMLElement>>;
|
|
20
|
+
export interface DropdownMenuContentProps extends Omit<HTMLMotionProps<'div'>, 'children'> {
|
|
21
|
+
children: React.ReactNode;
|
|
22
|
+
align?: 'start' | 'end';
|
|
23
|
+
side?: RootHorizontalPlacement;
|
|
24
|
+
sideOffset?: number;
|
|
25
|
+
animate?: boolean;
|
|
26
|
+
}
|
|
27
|
+
export declare const DropdownMenuContent: React.FC<DropdownMenuContentProps>;
|
|
28
|
+
export interface DropdownMenuItemProps extends React.HTMLAttributes<HTMLElement> {
|
|
29
|
+
children: React.ReactNode;
|
|
30
|
+
asChild?: boolean;
|
|
31
|
+
disabled?: boolean;
|
|
32
|
+
closeOnSelect?: boolean;
|
|
33
|
+
}
|
|
34
|
+
export declare const DropdownMenuItem: React.ForwardRefExoticComponent<DropdownMenuItemProps & React.RefAttributes<HTMLElement>>;
|
|
35
|
+
export interface DropdownMenuSubProps {
|
|
36
|
+
children: React.ReactNode;
|
|
37
|
+
defaultOpen?: boolean;
|
|
38
|
+
placement?: SubmenuHorizontalPlacement;
|
|
39
|
+
}
|
|
40
|
+
export declare const DropdownMenuSub: React.FC<DropdownMenuSubProps>;
|
|
41
|
+
export interface DropdownMenuSubTriggerProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
42
|
+
asChild?: boolean;
|
|
43
|
+
children: React.ReactNode;
|
|
44
|
+
}
|
|
45
|
+
export declare const DropdownMenuSubTrigger: React.ForwardRefExoticComponent<DropdownMenuSubTriggerProps & React.RefAttributes<HTMLElement>>;
|
|
46
|
+
export interface DropdownMenuSubContentProps extends Omit<HTMLMotionProps<'div'>, 'children'> {
|
|
47
|
+
children: React.ReactNode;
|
|
48
|
+
sideOffset?: number;
|
|
49
|
+
animate?: boolean;
|
|
50
|
+
}
|
|
51
|
+
export declare const DropdownMenuSubContent: React.FC<DropdownMenuSubContentProps>;
|
|
52
|
+
export {};
|