diff --git a/src/ui/components/VibeCard.tsx b/src/ui/components/VibeCard.tsx index ebcad6d..29b5c41 100644 --- a/src/ui/components/VibeCard.tsx +++ b/src/ui/components/VibeCard.tsx @@ -1,4 +1,20 @@ import React from 'react' +import { MoreHorizontal } from 'lucide-react' +import { Button } from './ui/button' +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from './ui/dropdown-menu' +import { cn } from '../lib/utils' + +export interface VibeCardAction { + name: string + onClick: () => void + variant?: 'default' | 'destructive' + icon?: React.ReactNode +} interface VibeCardProps { image?: string @@ -7,7 +23,7 @@ interface VibeCardProps { color: string onClick?: () => void index: number - action?: React.ReactNode + actions?: VibeCardAction[] } export const VibeCard: React.FC = ({ @@ -17,7 +33,7 @@ export const VibeCard: React.FC = ({ color, onClick, index, - action, + actions = [], }) => { return (
= ({ {/* 3. Content Layer - Stays fixed size relative to parent, creating the cutout effect */}
- {action && ( + {actions.length > 0 && (
e.stopPropagation()} > - {action} + + + + + + {actions.map((action, i) => ( + { + e.stopPropagation() + action.onClick() + }} + className={cn( + action.variant === 'destructive' && + 'text-red-600 focus:text-red-600 focus:bg-red-50', + )} + > + {action.icon && {action.icon}} + {action.name} + + ))} + +
)}
diff --git a/src/ui/pages/Characters.tsx b/src/ui/pages/Characters.tsx index 18183df..d8a561e 100644 --- a/src/ui/pages/Characters.tsx +++ b/src/ui/pages/Characters.tsx @@ -1,5 +1,5 @@ import { InferResponseType } from 'hono/client' -import { MoreHorizontal, Plus, Trash2 } from 'lucide-react' +import { Plus, Trash2 } from 'lucide-react' import { useState } from 'react' import { useNavigate } from 'react-router-dom' import { toast } from 'sonner' @@ -9,12 +9,6 @@ import { DeleteConfirmDialog } from '../components/DeleteConfirmDialog' import { PageHeader } from '../components/PageHeader' import { VibeCard } from '../components/VibeCard' import { Button } from '../components/ui/button' -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, -} from '../components/ui/dropdown-menu' import { useInfiniteScroll } from '../hooks/use-infinite-scroll' type CharacterResponse = InferResponseType @@ -90,31 +84,14 @@ export default function Characters() { image={char.image?.path ? `/files/${char.image.path}` : ''} color={getCharacterColor(char.name)} onClick={() => navigate(`/characters/${char.id}/looks`)} - action={ - - - - - - { - e.stopPropagation() - setDeleteId(char.id) - }} - className="text-red-600 focus:text-red-600 focus:bg-red-50" - > - - Delete - - - - } + actions={[ + { + name: 'Delete', + onClick: () => setDeleteId(char.id), + variant: 'destructive', + icon: , + }, + ]} /> ))}
diff --git a/src/ui/pages/Expressions.tsx b/src/ui/pages/Expressions.tsx index edfd59b..8cd078f 100644 --- a/src/ui/pages/Expressions.tsx +++ b/src/ui/pages/Expressions.tsx @@ -1,5 +1,5 @@ import { InferResponseType } from 'hono/client' -import { MoreHorizontal, Plus, Trash2 } from 'lucide-react' +import { Plus, Trash2 } from 'lucide-react' import { useState } from 'react' import { toast } from 'sonner' import { client } from '../client' @@ -8,12 +8,6 @@ import { DeleteConfirmDialog } from '../components/DeleteConfirmDialog' import { PageHeader } from '../components/PageHeader' import { VibeCard } from '../components/VibeCard' import { Button } from '../components/ui/button' -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, -} from '../components/ui/dropdown-menu' import { useInfiniteScroll } from '../hooks/use-infinite-scroll' type ExpressionResponse = InferResponseType @@ -73,32 +67,17 @@ export default function Expressions() { image={expression.imageUrl} color={expression.type === 'builtin' ? '#10b981' : '#f59e0b'} onClick={() => {}} - action={ - expression.type === 'custom' ? ( - - - - - - { - e.stopPropagation() - setDeleteId(expression.id) - }} - className="text-red-600 focus:text-red-600 focus:bg-red-50" - > - - Delete - - - - ) : null + actions={ + expression.type === 'custom' + ? [ + { + name: 'Delete', + onClick: () => setDeleteId(expression.id), + variant: 'destructive', + icon: , + }, + ] + : [] } />
diff --git a/src/ui/pages/Poses.tsx b/src/ui/pages/Poses.tsx index b2706f5..6e1e92d 100644 --- a/src/ui/pages/Poses.tsx +++ b/src/ui/pages/Poses.tsx @@ -1,5 +1,5 @@ import { InferResponseType } from 'hono/client' -import { MoreHorizontal, Plus, Trash2 } from 'lucide-react' +import { Plus, Trash2 } from 'lucide-react' import { useState } from 'react' import { toast } from 'sonner' import { client } from '../client' @@ -8,12 +8,6 @@ import { DeleteConfirmDialog } from '../components/DeleteConfirmDialog' import { PageHeader } from '../components/PageHeader' import { VibeCard } from '../components/VibeCard' import { Button } from '../components/ui/button' -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, -} from '../components/ui/dropdown-menu' import { useInfiniteScroll } from '../hooks/use-infinite-scroll' type PoseResponse = InferResponseType @@ -73,32 +67,17 @@ export default function Poses() { image={pose.imageUrl} color={pose.type === 'builtin' ? '#3b82f6' : '#f59e0b'} onClick={() => {}} - action={ - pose.type === 'custom' ? ( - - - - - - { - e.stopPropagation() - setDeleteId(pose.id) - }} - className="text-red-600 focus:text-red-600 focus:bg-red-50" - > - - Delete - - - - ) : null + actions={ + pose.type === 'custom' + ? [ + { + name: 'Delete', + onClick: () => setDeleteId(pose.id), + variant: 'destructive', + icon: , + }, + ] + : [] } />