Components
Loading preview...
Here is ContextMenu by indevui
npx shadcn@latest add https://21st.dev/r/ln-dev7/issue-context-menu// demo.tsx
'use client';
import * as React from 'react';
import { toast } from 'sonner';
import {
AlertCircle,
BarChartHorizontal,
SignalHigh,
SignalLow,
SignalMedium,
LucideIcon,
} from 'lucide-react';
import IssueContextMenu, {
ToDoIcon,
InProgressIcon,
CompletedIcon,
BacklogIcon,
PausedIcon,
} from '@/components/ui/issue-context-menu';
import type { Status, Priority, User, Label } from '@/components/ui/issue-context-menu';
import { ContextMenu, ContextMenuTrigger } from '@/components/ui/context-menu';
const statusList: Status[] = [
{ id: 'backlog', name: 'Backlog', icon: BacklogIcon, color: '#a1a1aa' },
{ id: 'todo', name: 'To-Do', icon: ToDoIcon, color: '#f97316' },
{ id: 'in-progress', name: 'In Progress', icon: InProgressIcon, color: '#facc15' },
{ id: 'paused', name: 'Paused', icon: PausedIcon, color: '#0ea5e9' },
{ id: 'completed', name: 'Completed', icon: CompletedIcon, color: '#8b5cf6' },
];
const priorityList: Priority[] = [
{ id: 'no-priority', name: 'No Priority', icon: BarChartHorizontal },
{ id: 'low', name: 'Low', icon: SignalLow },
{ id: 'medium', name: 'Medium', icon: SignalMedium },
{ id: 'high', name: 'High', icon: SignalHigh },
{ id: 'urgent', name: 'Urgent', icon: AlertCircle },
];
const userList: User[] = [
{ id: 'usr_1', name: 'Alice', avatarUrl: 'https://i.pravatar.cc/150?u=alice' },
{ id: 'usr_2', name: 'Bob', avatarUrl: 'https://i.pravatar.cc/150?u=bob' },
{ id: 'usr_3', name: 'Charlie', avatarUrl: 'https://i.pravatar.cc/150?u=charlie' },
];
const labelList: Label[] = [
{ id: 'lbl_1', name: 'Bug', color: '#d73a4a' },
{ id: 'lbl_2', name: 'Feature', color: '#0e8a16' },
{ id: 'lbl_3', name: 'Design', color: '#d876e3' },
];
const IssueContextMenuDemo = () => {
const [isSubscribed, setIsSubscribed] = React.useState(false);
const [isFavorite, setIsFavorite] = React.useState(false);
const handleAction = (action: string, value?: any) => {
switch (action) {
case 'status':
toast.success(`Status changed to ${statusList.find((s) => s.id === value)?.name}`);
break;
case 'assignee':
const name = value ? userList.find((u) => u.id === value)?.name : 'Unassigned';
toast.success(`Assigned to ${name}`);
break;
case 'priority':
toast.success(`Priority set to ${priorityList.find((p) => p.id === value)?.name}`);
break;
case 'label':
toast.info(`Toggled label: ${labelList.find((l) => l.id === value)?.name}`);
break;
case 'subscribe':
setIsSubscribed((prev) => !prev);
toast.success(isSubscribed ? 'Unsubscribed from issue' : 'Subscribed to issue');
break;
case 'favorite':
setIsFavorite((prev) => !prev);
toast.success(isFavorite ? 'Removed from favorites' : 'Added to favorites');
break;
case 'copyId':
navigator.clipboard.writeText('ISSUE-123');
toast.success('Issue ID copied to clipboard');
break;
case 'delete':
toast.error('Issue has been deleted');
break;
default:
toast('Action triggered', { description: `${action} ${value || ''}` });
}
};
return (
<div className="flex w-full items-center justify-center p-8">
<ContextMenu>
<ContextMenuTrigger>
<div className="flex w-full max-w-sm cursor-context-menu flex-col items-center justify-center rounded-lg border-2 border-dashed border-border p-12 text-center">
<h3 className="font-medium">Right-Click Me</h3>
<p className="text-sm text-muted-foreground">
An issue context menu will appear.
</p>
</div>
</ContextMenuTrigger>
<IssueContextMenu
statusList={statusList}
priorityList={priorityList}
userList={userList}
labelList={labelList}
isSubscribed={isSubscribed}
isFavorite={isFavorite}
onAction={handleAction}
/>
</ContextMenu>
</div>
);
};
export { IssueContextMenuDemo as DemoOne };