@rovula/ui 0.1.21 → 0.1.22
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/dist/cjs/bundle.css +175 -26
- package/dist/cjs/bundle.js +675 -675
- package/dist/cjs/bundle.js.map +1 -1
- package/dist/cjs/types/components/Badge/Badge.d.ts +40 -0
- package/dist/cjs/types/components/Badge/Badge.stories.d.ts +295 -0
- package/dist/cjs/types/components/Badge/Badge.styles.d.ts +7 -0
- package/dist/cjs/types/components/Badge/index.d.ts +2 -0
- package/dist/cjs/types/components/Dropdown/Dropdown.d.ts +4 -8
- package/dist/cjs/types/components/Dropdown/Dropdown.stories.d.ts +1 -6
- package/dist/cjs/types/components/DropdownMenu/DropdownMenu.d.ts +5 -1
- package/dist/cjs/types/components/DropdownMenu/DropdownMenu.stories.d.ts +16 -0
- package/dist/cjs/types/index.d.ts +3 -1
- package/dist/cjs/types/patterns/menu/Menu.d.ts +70 -0
- package/dist/cjs/types/{components/Menu → patterns/menu}/Menu.stories.d.ts +17 -10
- package/dist/cjs/types/utils/mergeRefs.d.ts +20 -0
- package/dist/components/Avatar/Avatar.styles.js +2 -2
- package/dist/components/Badge/Badge.js +36 -0
- package/dist/components/Badge/Badge.stories.js +51 -0
- package/dist/components/Badge/Badge.styles.js +62 -0
- package/dist/components/Badge/index.js +2 -0
- package/dist/components/Dropdown/Dropdown.js +54 -163
- package/dist/components/Dropdown/Dropdown.stories.js +29 -0
- package/dist/components/DropdownMenu/DropdownMenu.js +22 -9
- package/dist/components/DropdownMenu/DropdownMenu.stories.js +54 -10
- package/dist/components/TextInput/TextInput.js +6 -3
- package/dist/esm/bundle.css +175 -26
- package/dist/esm/bundle.js +1545 -1545
- package/dist/esm/bundle.js.map +1 -1
- package/dist/esm/types/components/Badge/Badge.d.ts +40 -0
- package/dist/esm/types/components/Badge/Badge.stories.d.ts +295 -0
- package/dist/esm/types/components/Badge/Badge.styles.d.ts +7 -0
- package/dist/esm/types/components/Badge/index.d.ts +2 -0
- package/dist/esm/types/components/Dropdown/Dropdown.d.ts +4 -8
- package/dist/esm/types/components/Dropdown/Dropdown.stories.d.ts +1 -6
- package/dist/esm/types/components/DropdownMenu/DropdownMenu.d.ts +5 -1
- package/dist/esm/types/components/DropdownMenu/DropdownMenu.stories.d.ts +16 -0
- package/dist/esm/types/index.d.ts +3 -1
- package/dist/esm/types/patterns/menu/Menu.d.ts +70 -0
- package/dist/esm/types/{components/Menu → patterns/menu}/Menu.stories.d.ts +17 -10
- package/dist/esm/types/utils/mergeRefs.d.ts +20 -0
- package/dist/index.d.ts +116 -73
- package/dist/index.js +2 -1
- package/dist/patterns/menu/Menu.js +95 -0
- package/dist/patterns/menu/Menu.stories.js +611 -0
- package/dist/src/theme/global.css +289 -37
- package/dist/utils/mergeRefs.js +42 -0
- package/package.json +1 -1
- package/src/components/Avatar/Avatar.styles.ts +2 -2
- package/src/components/Badge/Badge.stories.tsx +128 -0
- package/src/components/Badge/Badge.styles.ts +70 -0
- package/src/components/Badge/Badge.tsx +103 -0
- package/src/components/Badge/index.ts +3 -0
- package/src/components/Dropdown/Dropdown.stories.tsx +170 -1
- package/src/components/Dropdown/Dropdown.tsx +186 -276
- package/src/components/DropdownMenu/DropdownMenu.stories.tsx +1050 -113
- package/src/components/DropdownMenu/DropdownMenu.tsx +116 -52
- package/src/components/TextInput/TextInput.tsx +6 -3
- package/src/index.ts +3 -1
- package/src/patterns/menu/Menu.stories.tsx +1100 -0
- package/src/patterns/menu/Menu.tsx +282 -0
- package/src/theme/themes/xspector/baseline.css +0 -1
- package/src/theme/tokens/baseline.css +2 -1
- package/src/theme/tokens/components/badge.css +54 -0
- package/src/theme/tokens/components/dropdown-menu.css +15 -4
- package/src/utils/mergeRefs.ts +46 -0
- package/dist/cjs/types/components/Menu/Menu.d.ts +0 -65
- package/dist/cjs/types/components/Menu/helpers.d.ts +0 -19
- package/dist/cjs/types/components/Menu/index.d.ts +0 -4
- package/dist/components/Menu/Menu.js +0 -64
- package/dist/components/Menu/Menu.stories.js +0 -406
- package/dist/components/Menu/helpers.js +0 -28
- package/dist/components/Menu/index.js +0 -3
- package/dist/esm/types/components/Menu/Menu.d.ts +0 -65
- package/dist/esm/types/components/Menu/helpers.d.ts +0 -19
- package/dist/esm/types/components/Menu/index.d.ts +0 -4
- package/src/components/Menu/Menu.stories.tsx +0 -586
- package/src/components/Menu/Menu.tsx +0 -235
- package/src/components/Menu/helpers.ts +0 -45
- package/src/components/Menu/index.ts +0 -7
- package/src/theme/themes/xspector/components/dropdown-menu.css +0 -28
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
3
|
+
import { Badge, SeverityBadge } from "./Badge";
|
|
4
|
+
import type { BadgeColor, SeverityLevel } from "./Badge";
|
|
5
|
+
|
|
6
|
+
const meta = {
|
|
7
|
+
title: "Components/Badge",
|
|
8
|
+
component: Badge,
|
|
9
|
+
tags: ["autodocs"],
|
|
10
|
+
parameters: {
|
|
11
|
+
layout: "fullscreen",
|
|
12
|
+
},
|
|
13
|
+
decorators: [
|
|
14
|
+
(Story) => (
|
|
15
|
+
<div className="p-8 bg-bg-bg1">
|
|
16
|
+
<Story />
|
|
17
|
+
</div>
|
|
18
|
+
),
|
|
19
|
+
],
|
|
20
|
+
} satisfies Meta<typeof Badge>;
|
|
21
|
+
|
|
22
|
+
export default meta;
|
|
23
|
+
type Story = StoryObj<typeof meta>;
|
|
24
|
+
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
// Default — single badge with controls
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
|
|
29
|
+
export const Default: Story = {
|
|
30
|
+
args: {
|
|
31
|
+
label: "To do",
|
|
32
|
+
color: "default",
|
|
33
|
+
clickable: false,
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
// Status Badges — all colors × clickable / static
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
|
|
41
|
+
const COLORS: BadgeColor[] = ["default", "warning", "info", "error", "success"];
|
|
42
|
+
|
|
43
|
+
const COLOR_LABELS: Record<BadgeColor, string> = {
|
|
44
|
+
default: "To do",
|
|
45
|
+
warning: "In Progress",
|
|
46
|
+
info: "Ready to review",
|
|
47
|
+
error: "In review",
|
|
48
|
+
success: "Completed",
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const StatusBadges: StoryObj = {
|
|
52
|
+
render: () => (
|
|
53
|
+
<div className="flex flex-col gap-6">
|
|
54
|
+
{/* Clickable (with border + chevron) */}
|
|
55
|
+
<div>
|
|
56
|
+
<p className="typography-small1 text-text-g-contrast-low mb-3">
|
|
57
|
+
Clickable
|
|
58
|
+
</p>
|
|
59
|
+
<div className="flex flex-wrap gap-3">
|
|
60
|
+
{COLORS.map((color) => (
|
|
61
|
+
<Badge
|
|
62
|
+
key={color}
|
|
63
|
+
color={color}
|
|
64
|
+
label={COLOR_LABELS[color]}
|
|
65
|
+
clickable
|
|
66
|
+
/>
|
|
67
|
+
))}
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
{/* Static (no border, no chevron) */}
|
|
72
|
+
<div>
|
|
73
|
+
<p className="typography-small1 text-text-g-contrast-low mb-3">
|
|
74
|
+
Static
|
|
75
|
+
</p>
|
|
76
|
+
<div className="flex flex-wrap gap-3">
|
|
77
|
+
{COLORS.map((color) => (
|
|
78
|
+
<Badge key={color} color={color} label={COLOR_LABELS[color]} />
|
|
79
|
+
))}
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
|
|
83
|
+
{/* Percent only */}
|
|
84
|
+
<div>
|
|
85
|
+
<p className="typography-small1 text-text-g-contrast-low mb-3">
|
|
86
|
+
With percentage
|
|
87
|
+
</p>
|
|
88
|
+
<div className="flex flex-wrap gap-3">
|
|
89
|
+
{COLORS.map((color, i) => (
|
|
90
|
+
<Badge
|
|
91
|
+
key={color}
|
|
92
|
+
color={color}
|
|
93
|
+
label={COLOR_LABELS[color]}
|
|
94
|
+
percent={i === 0 ? 0 : i === 4 ? 100 : 50}
|
|
95
|
+
/>
|
|
96
|
+
))}
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
),
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
// Severity Badges
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
|
|
107
|
+
const SEVERITIES: SeverityLevel[] = [
|
|
108
|
+
"highest",
|
|
109
|
+
"high",
|
|
110
|
+
"medium",
|
|
111
|
+
"low",
|
|
112
|
+
"lowest",
|
|
113
|
+
];
|
|
114
|
+
|
|
115
|
+
export const SeverityBadges: StoryObj = {
|
|
116
|
+
render: () => (
|
|
117
|
+
<div className="flex flex-col gap-3">
|
|
118
|
+
<p className="typography-small1 text-text-g-contrast-low mb-1">
|
|
119
|
+
Severity levels
|
|
120
|
+
</p>
|
|
121
|
+
<div className="flex flex-wrap gap-3">
|
|
122
|
+
{SEVERITIES.map((severity) => (
|
|
123
|
+
<SeverityBadge key={severity} severity={severity} />
|
|
124
|
+
))}
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
127
|
+
),
|
|
128
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { cva } from "class-variance-authority";
|
|
2
|
+
|
|
3
|
+
export const badgeVariants = cva(
|
|
4
|
+
[
|
|
5
|
+
"inline-flex items-center justify-center rounded-lg px-3 py-1",
|
|
6
|
+
"typography-body3",
|
|
7
|
+
],
|
|
8
|
+
{
|
|
9
|
+
variants: {
|
|
10
|
+
color: {
|
|
11
|
+
default: [
|
|
12
|
+
"bg-[var(--badge-default-bg)]",
|
|
13
|
+
"text-[var(--badge-default-text)]",
|
|
14
|
+
],
|
|
15
|
+
success: [
|
|
16
|
+
"bg-[var(--badge-success-bg)]",
|
|
17
|
+
"text-[var(--badge-success-text)]",
|
|
18
|
+
],
|
|
19
|
+
warning: [
|
|
20
|
+
"bg-[var(--badge-warning-bg)]",
|
|
21
|
+
"text-[var(--badge-warning-text)]",
|
|
22
|
+
],
|
|
23
|
+
info: [
|
|
24
|
+
"bg-[var(--badge-info-bg)]",
|
|
25
|
+
"text-[var(--badge-info-text)]",
|
|
26
|
+
],
|
|
27
|
+
error: [
|
|
28
|
+
"bg-[var(--badge-error-bg)]",
|
|
29
|
+
"text-[var(--badge-error-text)]",
|
|
30
|
+
],
|
|
31
|
+
},
|
|
32
|
+
clickable: {
|
|
33
|
+
true: "border border-solid",
|
|
34
|
+
false: "",
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
compoundVariants: [
|
|
38
|
+
{ color: "default", clickable: true, className: "border-[var(--badge-default-border)]" },
|
|
39
|
+
{ color: "success", clickable: true, className: "border-[var(--badge-success-border)]" },
|
|
40
|
+
{ color: "warning", clickable: true, className: "border-[var(--badge-warning-border)]" },
|
|
41
|
+
{ color: "info", clickable: true, className: "border-[var(--badge-info-border)]" },
|
|
42
|
+
{ color: "error", clickable: true, className: "border-[var(--badge-error-border)]" },
|
|
43
|
+
],
|
|
44
|
+
defaultVariants: {
|
|
45
|
+
color: "default",
|
|
46
|
+
clickable: false,
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
export const severityBadgeVariants = cva(
|
|
52
|
+
[
|
|
53
|
+
"inline-flex items-center justify-center rounded px-1 py-0.5",
|
|
54
|
+
"typography-small6 text-[var(--badge-severity-text)]",
|
|
55
|
+
],
|
|
56
|
+
{
|
|
57
|
+
variants: {
|
|
58
|
+
severity: {
|
|
59
|
+
highest: "bg-[var(--badge-severity-highest-bg)]",
|
|
60
|
+
high: "bg-[var(--badge-severity-high-bg)]",
|
|
61
|
+
medium: "bg-[var(--badge-severity-medium-bg)]",
|
|
62
|
+
low: "bg-[var(--badge-severity-low-bg)]",
|
|
63
|
+
lowest: "bg-[var(--badge-severity-lowest-bg)]",
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
defaultVariants: {
|
|
67
|
+
severity: "medium",
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
);
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { ChevronDownIcon } from "@heroicons/react/16/solid";
|
|
5
|
+
import { cn } from "@/utils/cn";
|
|
6
|
+
import { badgeVariants, severityBadgeVariants } from "./Badge.styles";
|
|
7
|
+
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// Status Badge
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
export type BadgeColor = "default" | "success" | "warning" | "info" | "error";
|
|
13
|
+
|
|
14
|
+
export type BadgeProps = {
|
|
15
|
+
/** Badge text label */
|
|
16
|
+
label: string;
|
|
17
|
+
/** Color variant */
|
|
18
|
+
color?: BadgeColor;
|
|
19
|
+
/**
|
|
20
|
+
* Show a dropdown chevron — use when the badge acts as a clickable trigger.
|
|
21
|
+
* Automatically adds a border to indicate interactivity.
|
|
22
|
+
*/
|
|
23
|
+
clickable?: boolean;
|
|
24
|
+
/** Optional percentage value shown below the label */
|
|
25
|
+
percent?: number;
|
|
26
|
+
className?: string;
|
|
27
|
+
} & React.HTMLAttributes<HTMLSpanElement>;
|
|
28
|
+
|
|
29
|
+
const Badge = React.forwardRef<HTMLSpanElement, BadgeProps>(
|
|
30
|
+
(
|
|
31
|
+
{
|
|
32
|
+
label,
|
|
33
|
+
color = "default",
|
|
34
|
+
clickable = false,
|
|
35
|
+
percent,
|
|
36
|
+
className,
|
|
37
|
+
...props
|
|
38
|
+
},
|
|
39
|
+
ref,
|
|
40
|
+
) => {
|
|
41
|
+
const hasPercent = percent !== undefined;
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<span
|
|
45
|
+
ref={ref}
|
|
46
|
+
className={cn(
|
|
47
|
+
badgeVariants({ color, clickable }),
|
|
48
|
+
hasPercent && "flex-col gap-0.5",
|
|
49
|
+
clickable && "cursor-pointer gap-1",
|
|
50
|
+
className,
|
|
51
|
+
)}
|
|
52
|
+
{...props}
|
|
53
|
+
>
|
|
54
|
+
<span className="flex items-center gap-1">
|
|
55
|
+
{label}
|
|
56
|
+
{clickable && (
|
|
57
|
+
<ChevronDownIcon className="size-4 shrink-0" aria-hidden />
|
|
58
|
+
)}
|
|
59
|
+
</span>
|
|
60
|
+
{hasPercent && (
|
|
61
|
+
<span className="tabular-nums">
|
|
62
|
+
{percent}%
|
|
63
|
+
</span>
|
|
64
|
+
)}
|
|
65
|
+
</span>
|
|
66
|
+
);
|
|
67
|
+
},
|
|
68
|
+
);
|
|
69
|
+
Badge.displayName = "Badge";
|
|
70
|
+
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
// Severity Badge
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
|
|
75
|
+
export type SeverityLevel = "highest" | "high" | "medium" | "low" | "lowest";
|
|
76
|
+
|
|
77
|
+
export type SeverityBadgeProps = {
|
|
78
|
+
severity: SeverityLevel;
|
|
79
|
+
className?: string;
|
|
80
|
+
} & React.HTMLAttributes<HTMLSpanElement>;
|
|
81
|
+
|
|
82
|
+
const SEVERITY_LABELS: Record<SeverityLevel, string> = {
|
|
83
|
+
highest: "Highest",
|
|
84
|
+
high: "High",
|
|
85
|
+
medium: "Medium",
|
|
86
|
+
low: "Low",
|
|
87
|
+
lowest: "Lowest",
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const SeverityBadge = React.forwardRef<HTMLSpanElement, SeverityBadgeProps>(
|
|
91
|
+
({ severity, className, ...props }, ref) => (
|
|
92
|
+
<span
|
|
93
|
+
ref={ref}
|
|
94
|
+
className={cn(severityBadgeVariants({ severity }), className)}
|
|
95
|
+
{...props}
|
|
96
|
+
>
|
|
97
|
+
{SEVERITY_LABELS[severity]}
|
|
98
|
+
</span>
|
|
99
|
+
),
|
|
100
|
+
);
|
|
101
|
+
SeverityBadge.displayName = "SeverityBadge";
|
|
102
|
+
|
|
103
|
+
export { Badge, SeverityBadge };
|
|
@@ -3,8 +3,18 @@ import type { Meta, StoryObj } from "@storybook/react";
|
|
|
3
3
|
import Dropdown, { Options } from "./Dropdown";
|
|
4
4
|
import Button from "../Button/Button";
|
|
5
5
|
import { cn } from "@/utils/cn";
|
|
6
|
-
import { MenuItemType } from "../Menu/Menu";
|
|
7
6
|
import Icon from "../Icon/Icon";
|
|
7
|
+
import {
|
|
8
|
+
Dialog,
|
|
9
|
+
DialogContent,
|
|
10
|
+
DialogHeader,
|
|
11
|
+
DialogTitle,
|
|
12
|
+
DialogDescription,
|
|
13
|
+
DialogBody,
|
|
14
|
+
DialogFooter,
|
|
15
|
+
DialogTrigger,
|
|
16
|
+
DialogClose,
|
|
17
|
+
} from "../Dialog/Dialog";
|
|
8
18
|
|
|
9
19
|
// More on how to set up stories at: https://storybook.js.org/docs/7.0/react/writing-stories/introduction
|
|
10
20
|
const meta = {
|
|
@@ -198,3 +208,162 @@ export const WithIcons: StoryObj<typeof Dropdown> = {
|
|
|
198
208
|
);
|
|
199
209
|
},
|
|
200
210
|
};
|
|
211
|
+
|
|
212
|
+
// ---------------------------------------------------------------------------
|
|
213
|
+
// Dropdown inside Dialog — showcases the fix for overflow:hidden bug
|
|
214
|
+
// ---------------------------------------------------------------------------
|
|
215
|
+
|
|
216
|
+
const dialogOptions: Options[] = [
|
|
217
|
+
{ value: "design", label: "Design" },
|
|
218
|
+
{ value: "engineering", label: "Engineering" },
|
|
219
|
+
{ value: "product", label: "Product" },
|
|
220
|
+
{ value: "marketing", label: "Marketing" },
|
|
221
|
+
{ value: "data", label: "Data & Analytics" },
|
|
222
|
+
{ value: "ops", label: "Operations" },
|
|
223
|
+
];
|
|
224
|
+
|
|
225
|
+
const filterableOptions: Options[] = new Array(20).fill("").map((_, i) => ({
|
|
226
|
+
value: `member-${i + 1}`,
|
|
227
|
+
label: `Team Member ${i + 1}`,
|
|
228
|
+
}));
|
|
229
|
+
|
|
230
|
+
export const InsideDialog: StoryObj<typeof Dropdown> = {
|
|
231
|
+
name: "Inside Dialog",
|
|
232
|
+
render: () => {
|
|
233
|
+
const [department, setDepartment] = useState<Options | undefined>();
|
|
234
|
+
const [member, setMember] = useState<Options | undefined>();
|
|
235
|
+
const [role, setRole] = useState<Options | undefined>();
|
|
236
|
+
|
|
237
|
+
return (
|
|
238
|
+
<div className="flex gap-4 flex-wrap">
|
|
239
|
+
{/* Demo 1: Single dropdown in dialog */}
|
|
240
|
+
<div>
|
|
241
|
+
<p className="typography-small4 text-text-g-contrast-medium mb-2">
|
|
242
|
+
Single Dropdown
|
|
243
|
+
</p>
|
|
244
|
+
<Dialog>
|
|
245
|
+
<DialogTrigger asChild>
|
|
246
|
+
<Button variant="outline">Open Dialog</Button>
|
|
247
|
+
</DialogTrigger>
|
|
248
|
+
<DialogContent showCloseButton>
|
|
249
|
+
<DialogHeader>
|
|
250
|
+
<DialogTitle>Assign to Department</DialogTitle>
|
|
251
|
+
<DialogDescription>
|
|
252
|
+
Dropdown popup appears above the dialog overlay — not clipped
|
|
253
|
+
by overflow.
|
|
254
|
+
</DialogDescription>
|
|
255
|
+
</DialogHeader>
|
|
256
|
+
<DialogBody className="gap-4 py-2">
|
|
257
|
+
<Dropdown
|
|
258
|
+
id="dept"
|
|
259
|
+
label="Department"
|
|
260
|
+
size="md"
|
|
261
|
+
fullwidth
|
|
262
|
+
options={dialogOptions}
|
|
263
|
+
value={department}
|
|
264
|
+
onSelect={setDepartment}
|
|
265
|
+
/>
|
|
266
|
+
</DialogBody>
|
|
267
|
+
<DialogFooter>
|
|
268
|
+
<DialogClose asChild>
|
|
269
|
+
<Button variant="outline">Cancel</Button>
|
|
270
|
+
</DialogClose>
|
|
271
|
+
<Button>Confirm</Button>
|
|
272
|
+
</DialogFooter>
|
|
273
|
+
</DialogContent>
|
|
274
|
+
</Dialog>
|
|
275
|
+
</div>
|
|
276
|
+
|
|
277
|
+
{/* Demo 2: Multiple dropdowns — each opens independently */}
|
|
278
|
+
<div>
|
|
279
|
+
<p className="typography-small4 text-text-g-contrast-medium mb-2">
|
|
280
|
+
Multiple Dropdowns
|
|
281
|
+
</p>
|
|
282
|
+
<Dialog>
|
|
283
|
+
<DialogTrigger asChild>
|
|
284
|
+
<Button variant="outline">Open Dialog</Button>
|
|
285
|
+
</DialogTrigger>
|
|
286
|
+
<DialogContent showCloseButton>
|
|
287
|
+
<DialogHeader>
|
|
288
|
+
<DialogTitle>Invite Team Member</DialogTitle>
|
|
289
|
+
<DialogDescription>
|
|
290
|
+
Multiple dropdowns — each opens its own popup independently.
|
|
291
|
+
</DialogDescription>
|
|
292
|
+
</DialogHeader>
|
|
293
|
+
<DialogBody className="gap-4 py-2">
|
|
294
|
+
<Dropdown
|
|
295
|
+
id="member"
|
|
296
|
+
label="Member"
|
|
297
|
+
size="md"
|
|
298
|
+
fullwidth
|
|
299
|
+
options={filterableOptions}
|
|
300
|
+
value={member}
|
|
301
|
+
onSelect={setMember}
|
|
302
|
+
filterMode
|
|
303
|
+
/>
|
|
304
|
+
<Dropdown
|
|
305
|
+
id="role"
|
|
306
|
+
label="Role"
|
|
307
|
+
size="md"
|
|
308
|
+
fullwidth
|
|
309
|
+
options={[
|
|
310
|
+
{ value: "viewer", label: "Viewer" },
|
|
311
|
+
{ value: "editor", label: "Editor" },
|
|
312
|
+
{ value: "admin", label: "Admin" },
|
|
313
|
+
]}
|
|
314
|
+
value={role}
|
|
315
|
+
onSelect={setRole}
|
|
316
|
+
/>
|
|
317
|
+
</DialogBody>
|
|
318
|
+
<DialogFooter>
|
|
319
|
+
<DialogClose asChild>
|
|
320
|
+
<Button variant="outline">Cancel</Button>
|
|
321
|
+
</DialogClose>
|
|
322
|
+
<Button disabled={!member || !role}>Send Invite</Button>
|
|
323
|
+
</DialogFooter>
|
|
324
|
+
</DialogContent>
|
|
325
|
+
</Dialog>
|
|
326
|
+
</div>
|
|
327
|
+
|
|
328
|
+
{/* Demo 3: Filterable dropdown in dialog */}
|
|
329
|
+
<div>
|
|
330
|
+
<p className="typography-small4 text-text-g-contrast-medium mb-2">
|
|
331
|
+
Filter Mode
|
|
332
|
+
</p>
|
|
333
|
+
<Dialog>
|
|
334
|
+
<DialogTrigger asChild>
|
|
335
|
+
<Button variant="outline">Open Dialog</Button>
|
|
336
|
+
</DialogTrigger>
|
|
337
|
+
<DialogContent showCloseButton>
|
|
338
|
+
<DialogHeader>
|
|
339
|
+
<DialogTitle>Search & Select</DialogTitle>
|
|
340
|
+
<DialogDescription>
|
|
341
|
+
filterMode=true — type to filter options, popup stays properly
|
|
342
|
+
positioned.
|
|
343
|
+
</DialogDescription>
|
|
344
|
+
</DialogHeader>
|
|
345
|
+
<DialogBody className="gap-4 py-2">
|
|
346
|
+
<Dropdown
|
|
347
|
+
id="member-filter"
|
|
348
|
+
label="Search member"
|
|
349
|
+
size="md"
|
|
350
|
+
fullwidth
|
|
351
|
+
filterMode
|
|
352
|
+
options={filterableOptions}
|
|
353
|
+
value={member}
|
|
354
|
+
onSelect={setMember}
|
|
355
|
+
/>
|
|
356
|
+
</DialogBody>
|
|
357
|
+
<DialogFooter>
|
|
358
|
+
<DialogClose asChild>
|
|
359
|
+
<Button variant="outline">Cancel</Button>
|
|
360
|
+
</DialogClose>
|
|
361
|
+
<Button disabled={!member}>Select</Button>
|
|
362
|
+
</DialogFooter>
|
|
363
|
+
</DialogContent>
|
|
364
|
+
</Dialog>
|
|
365
|
+
</div>
|
|
366
|
+
</div>
|
|
367
|
+
);
|
|
368
|
+
},
|
|
369
|
+
};
|