feat: enhance Kanban components with new UI elements

- Added `ColumnHeader`, `EmptyState`, and `DropZone` components to improve the Kanban UI structure and user experience.
- Refactored `KanbanColumn` to utilize the new components, enhancing readability and maintainability.
- Updated `Card` component to support flexible props for shadow, border, and background, allowing for better customization across the application.
- Adjusted `SwimlanesBase` to incorporate the new `ColumnHeader` for consistent column representation.
This commit is contained in:
Julien Froidefond
2025-09-28 22:10:12 +02:00
parent 5a3d825b8e
commit 687d02ff3a
9 changed files with 499 additions and 97 deletions

View File

@@ -3,23 +3,54 @@ import { cn } from '@/lib/utils';
interface CardProps extends HTMLAttributes<HTMLDivElement> {
variant?: 'default' | 'elevated' | 'bordered' | 'column';
shadow?: 'none' | 'sm' | 'md' | 'lg';
border?: 'none' | 'default' | 'primary' | 'accent';
background?: 'default' | 'column' | 'muted';
}
const Card = forwardRef<HTMLDivElement, CardProps>(
({ className, variant = 'default', ...props }, ref) => {
const variants = {
default: 'bg-[var(--card)]/50 border border-[var(--border)]/50',
elevated: 'bg-[var(--card)]/80 border border-[var(--border)]/50 shadow-lg shadow-[var(--card)]/20',
bordered: 'bg-[var(--card)]/50 border border-[var(--primary)]/30 shadow-[var(--primary)]/10 shadow-lg',
column: 'bg-[var(--card-column)] border border-[var(--border)]/50 shadow-lg shadow-[var(--card)]/20'
({ className, variant = 'default', shadow = 'sm', border = 'default', background = 'default', ...props }, ref) => {
const backgrounds = {
default: 'bg-[var(--card)]',
column: 'bg-[var(--card-column)]',
muted: 'bg-[var(--muted)]/10'
};
const borders = {
none: '',
default: 'border border-[var(--border)]',
primary: 'border border-[var(--primary)]/30',
accent: 'border border-[var(--accent)]/30'
};
const shadows = {
none: '',
sm: 'shadow-sm',
md: 'shadow-md',
lg: 'shadow-lg'
};
// Variants prédéfinis pour la rétrocompatibilité
const variantStyles = {
default: '',
elevated: 'shadow-lg',
bordered: 'border-[var(--primary)]/30 shadow-lg',
column: 'bg-[var(--card-column)] shadow-lg'
};
// Appliquer le variant si spécifié, sinon utiliser les props individuelles
const finalShadow = variant !== 'default' ? variantStyles[variant] : shadows[shadow];
const finalBorder = variant !== 'default' ? variantStyles[variant] : borders[border];
const finalBackground = variant !== 'default' ? variantStyles[variant] : backgrounds[background];
return (
<div
ref={ref}
className={cn(
'rounded-lg backdrop-blur-sm transition-all duration-200',
variants[variant],
finalBackground,
finalBorder,
finalShadow,
className
)}
{...props}
@@ -30,50 +61,113 @@ const Card = forwardRef<HTMLDivElement, CardProps>(
Card.displayName = 'Card';
const CardHeader = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => (
<div
ref={ref}
className={cn('p-4 border-b border-[var(--border)]/50', className)}
{...props}
/>
)
interface CardHeaderProps extends HTMLAttributes<HTMLDivElement> {
separator?: boolean;
padding?: 'sm' | 'md' | 'lg';
}
const CardHeader = forwardRef<HTMLDivElement, CardHeaderProps>(
({ className, separator = true, padding = 'md', ...props }, ref) => {
const paddings = {
sm: 'p-2',
md: 'p-4',
lg: 'p-6'
};
return (
<div
ref={ref}
className={cn(
paddings[padding],
separator && 'border-b border-[var(--border)]',
className
)}
{...props}
/>
);
}
);
CardHeader.displayName = 'CardHeader';
const CardTitle = forwardRef<HTMLHeadingElement, HTMLAttributes<HTMLHeadingElement>>(
({ className, ...props }, ref) => (
<h3
ref={ref}
className={cn('font-mono font-semibold text-[var(--foreground)] tracking-wide', className)}
{...props}
/>
)
interface CardTitleProps extends HTMLAttributes<HTMLHeadingElement> {
size?: 'sm' | 'md' | 'lg';
}
const CardTitle = forwardRef<HTMLHeadingElement, CardTitleProps>(
({ className, size = 'md', ...props }, ref) => {
const sizes = {
sm: 'text-sm',
md: 'text-base',
lg: 'text-lg'
};
return (
<h3
ref={ref}
className={cn(
'font-mono font-semibold text-[var(--foreground)] tracking-wide',
sizes[size],
className
)}
{...props}
/>
);
}
);
CardTitle.displayName = 'CardTitle';
const CardContent = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => (
<div
ref={ref}
className={cn('p-4', className)}
{...props}
/>
)
interface CardContentProps extends HTMLAttributes<HTMLDivElement> {
padding?: 'sm' | 'md' | 'lg' | 'none';
}
const CardContent = forwardRef<HTMLDivElement, CardContentProps>(
({ className, padding = 'md', ...props }, ref) => {
const paddings = {
none: '',
sm: 'p-2',
md: 'p-4',
lg: 'p-6'
};
return (
<div
ref={ref}
className={cn(paddings[padding], className)}
{...props}
/>
);
}
);
CardContent.displayName = 'CardContent';
const CardFooter = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => (
<div
ref={ref}
className={cn('p-4 border-t border-[var(--border)]/50', className)}
{...props}
/>
)
interface CardFooterProps extends HTMLAttributes<HTMLDivElement> {
separator?: boolean;
padding?: 'sm' | 'md' | 'lg';
}
const CardFooter = forwardRef<HTMLDivElement, CardFooterProps>(
({ className, separator = true, padding = 'md', ...props }, ref) => {
const paddings = {
sm: 'p-2',
md: 'p-4',
lg: 'p-6'
};
return (
<div
ref={ref}
className={cn(
paddings[padding],
separator && 'border-t border-[var(--border)]',
className
)}
{...props}
/>
);
}
);
CardFooter.displayName = 'CardFooter';