import * as React from 'react';
import { Check, Circle } from 'lucide-react';
import { cn } from '@/lib/utils';
import { Dropdown } from './dropdown';
// Pure Kodo DropdownMenu implementation - No Radix UI dependency
// Uses the existing Dropdown component as base
/**
* DropdownMenuProps - Propriétés du composant DropdownMenu
*
* @interface DropdownMenuProps
*/
export interface DropdownMenuProps {
/**
* Si `true`, le menu est ouvert (mode contrôlé)
* Si non fourni, le menu gère son propre état (mode non-contrôlé)
*
* @example
* ```tsx
*
* Menu
* ...
*
* ```
*/
open?: boolean;
/**
* Fonction appelée lorsque l'état ouvert change
*
* @param {boolean} open - Nouvel état ouvert
*/
onOpenChange?: (open: boolean) => void;
/**
* Enfants du composant (DropdownMenuTrigger et DropdownMenuContent)
*/
children: React.ReactNode;
}
/**
* DropdownMenu - Composant de menu déroulant avec design system Kodo
*
* Composant de menu déroulant pour afficher des actions ou des options.
* Implémentation pure Kodo sans dépendance Radix UI.
*
* @example
* ```tsx
* // Menu déroulant simple
*
*
*
*
*
* Éditer
* Supprimer
*
*
* ```
*
* @component
* @param {DropdownMenuProps} props - Propriétés du composant
* @returns {JSX.Element} Menu déroulant avec trigger et contenu
*/
const DropdownMenu: React.FC = ({ open, onOpenChange, children }) => {
const [_internalOpen, setInternalOpen] = React.useState(false);
const isControlled = open !== undefined;
const handleOpenChange = (newOpen: boolean) => {
if (!isControlled) {
setInternalOpen(newOpen);
}
onOpenChange?.(newOpen);
};
// Extract trigger and content from children
const trigger = React.Children.toArray(children).find(
(child) => React.isValidElement(child) && child.type === DropdownMenuTrigger
);
const content = React.Children.toArray(children).find(
(child) => React.isValidElement(child) && child.type === DropdownMenuContent
);
if (!trigger || !content) {
return <>{children}>;
}
return (
{React.isValidElement(content) ? content.props.children : content}
);
};
export interface DropdownMenuTriggerProps extends React.HTMLAttributes {
asChild?: boolean;
}
const DropdownMenuTrigger = React.forwardRef(
({ className, children, asChild, ...props }, ref) => {
if (asChild && React.isValidElement(children)) {
return React.cloneElement(children, {
ref,
className: cn(className, children.props.className),
...props,
} as any);
}
return (
);
}
);
DropdownMenuTrigger.displayName = 'DropdownMenuTrigger';
export interface DropdownMenuContentProps extends React.HTMLAttributes {
align?: 'start' | 'end' | 'center';
sideOffset?: number;
}
const DropdownMenuContent = React.forwardRef(
({ className, align = 'start', sideOffset = 4, children, ...props }, ref) => {
return (
{children}
);
}
);
DropdownMenuContent.displayName = 'DropdownMenuContent';
export interface DropdownMenuItemProps extends React.ButtonHTMLAttributes {
inset?: boolean;
}
const DropdownMenuItem = React.forwardRef(
({ className, inset, onKeyDown, onClick, ...props }, ref) => {
// CRITIQUE FIX #47: Gestion complète du clavier pour l'accessibilité
const handleKeyDown = (e: React.KeyboardEvent) => {
// Gérer Enter et Space pour activer l'item
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
if (onClick && !props.disabled) {
onClick(e as any);
}
}
// Gérer Escape pour fermer le menu (géré par le composant parent Dropdown)
// Les flèches sont gérées par le composant Dropdown parent
// Appeler le handler personnalisé s'il existe
if (onKeyDown) {
onKeyDown(e);
}
};
return (
);
}
);
DropdownMenuItem.displayName = 'DropdownMenuItem';
export interface DropdownMenuCheckboxItemProps extends React.ButtonHTMLAttributes {
checked?: boolean;
onCheckedChange?: (checked: boolean) => void;
}
const DropdownMenuCheckboxItem = React.forwardRef(
({ className, children, checked, onCheckedChange, ...props }, ref) => (
)
);
DropdownMenuCheckboxItem.displayName = 'DropdownMenuCheckboxItem';
export interface DropdownMenuRadioItemProps extends React.ButtonHTMLAttributes {
value: string;
checked?: boolean;
}
const DropdownMenuRadioItem = React.forwardRef(
({ className, children, checked, ...props }, ref) => (
)
);
DropdownMenuRadioItem.displayName = 'DropdownMenuRadioItem';
export interface DropdownMenuLabelProps extends React.HTMLAttributes {
inset?: boolean;
}
const DropdownMenuLabel = React.forwardRef(
({ className, inset, ...props }, ref) => (
)
);
DropdownMenuLabel.displayName = 'DropdownMenuLabel';
export interface DropdownMenuSeparatorProps extends React.HTMLAttributes {}
const DropdownMenuSeparator = React.forwardRef(
({ className, ...props }, ref) => (
)
);
DropdownMenuSeparator.displayName = 'DropdownMenuSeparator';
const DropdownMenuShortcut = ({
className,
...props
}: React.HTMLAttributes) => {
return (
);
};
DropdownMenuShortcut.displayName = 'DropdownMenuShortcut';
// Placeholder components for compatibility (not fully implemented but exported)
const DropdownMenuGroup: React.FC<{ children: React.ReactNode }> = ({ children }) => <>{children}>;
const DropdownMenuPortal: React.FC<{ children: React.ReactNode }> = ({ children }) => <>{children}>;
const DropdownMenuSub: React.FC<{ children: React.ReactNode }> = ({ children }) => <>{children}>;
const DropdownMenuSubContent: React.FC<{ children: React.ReactNode }> = ({ children }) => <>{children}>;
const DropdownMenuSubTrigger: React.FC<{ children: React.ReactNode }> = ({ children }) => <>{children}>;
const DropdownMenuRadioGroup: React.FC<{ children: React.ReactNode }> = ({ children }) => <>{children}>;
export {
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuCheckboxItem,
DropdownMenuRadioItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuGroup,
DropdownMenuPortal,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuRadioGroup,
};