kanban / src /components /editor /layout /LayoutItem.tsx
Leon4gr45's picture
Deploy magic-resume as Docker Space (port 3000)
c30ea2a verified
Raw
History Blame Contribute Delete
6.32 kB
import { motion, Reorder, useDragControls } from "framer-motion";
import { Eye, EyeOff, GripVertical, Trash2 } from "lucide-react";
import { cn } from "@/lib/utils";
import { MenuSection } from "@/types/resume";
import { useTranslations } from "@/i18n/compat/client";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
interface LayoutItemProps {
item: MenuSection;
isBasic?: boolean;
activeSection: string;
setActiveSection: (id: string) => void;
toggleSectionVisibility: (id: string) => void;
updateMenuSections: (sections: MenuSection[]) => void;
removeCustomData: (sectionId: string) => void;
menuSections: MenuSection[];
}
const LayoutItem = ({
item,
isBasic = false,
activeSection,
setActiveSection,
toggleSectionVisibility,
updateMenuSections,
removeCustomData,
menuSections
}: LayoutItemProps) => {
const dragControls = useDragControls();
const t = useTranslations("common");
if (isBasic) {
return (
<div
className={cn(
"rounded-lg group border mb-2",
"bg-card border-border",
"hover:border-primary/50 transition-colors",
activeSection === item.id &&
"border-primary text-primary ring-1 ring-primary"
)}
onClick={() => setActiveSection(item.id)}
>
<div className="flex items-center p-3 pl-[32px] space-x-3">
<span
className={cn(
"text-lg ml-[12px]",
"text-muted-foreground group-hover:text-foreground transition-colors"
)}
>
{item.icon}
</span>
<span className={cn("text-sm flex-1 cursor-pointer")}>
{item.title}
</span>
</div>
</div>
);
}
return (
<Reorder.Item
id={item.id}
value={item}
dragListener={false}
dragControls={dragControls}
className={cn(
"rounded-lg group border flex overflow-hidden ",
"bg-card border-border",
"hover:border-primary/50 transition-colors",
activeSection === item.id &&
"border-primary text-primary ring-1 ring-primary"
)}
whileHover={{ scale: 1.01 }}
whileDrag={{ scale: 1.02 }}
>
<div
onPointerDown={(event) => {
dragControls.start(event);
}}
className={cn(
"w-8 flex items-center justify-center touch-none shrink-0",
"border-border",
"cursor-grab hover:bg-muted/50"
)}
>
<GripVertical
className={cn(
"w-4 h-4",
"text-muted-foreground",
"transform transition-transform group-hover:scale-110"
)}
/>
</div>
<div
className="flex select-none items-center p-3 space-x-3 flex-1 cursor-pointer"
onClick={() => setActiveSection(item.id)}
>
<div className="flex flex-1 items-center">
<span
className={cn(
"text-lg mr-2",
"text-muted-foreground group-hover:text-foreground transition-colors"
)}
>
{item.icon}
</span>
<span className="text-sm flex-1">{item.title}</span>
<motion.button
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.9 }}
onClick={(e) => {
e.stopPropagation();
toggleSectionVisibility(item.id);
}}
className={cn(
"p-1.5 rounded-md mr-2",
"hover:bg-muted text-muted-foreground hover:text-foreground transition-colors"
)}
>
{item.enabled ? (
<Eye className="w-4 h-4 text-primary" />
) : (
<EyeOff className="w-4 h-4" />
)}
</motion.button>
<AlertDialog>
<AlertDialogTrigger asChild>
<motion.button
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.9 }}
onClick={(e) => e.stopPropagation()}
className={cn(
"p-1.5 rounded-md text-primary",
"hover:bg-destructive/10 text-muted-foreground hover:text-destructive transition-colors"
)}
>
<Trash2 className="w-4 h-4 text-red-400" />
</motion.button>
</AlertDialogTrigger>
<AlertDialogContent onClick={(e) => e.stopPropagation()}>
<AlertDialogHeader>
<AlertDialogTitle>{t("delete")} {item.title}</AlertDialogTitle>
<AlertDialogDescription>
{t("deleteModuleConfirm")}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel onClick={(e) => e.stopPropagation()}>{t("cancel")}</AlertDialogCancel>
<AlertDialogAction
onClick={(e) => {
e.stopPropagation();
const updatedSections = menuSections.filter(
(section) => section.id !== item.id
);
const currentIndex = menuSections.findIndex(
(section) => section.id === item.id
);
const fallbackSection =
menuSections[currentIndex - 1] ?? updatedSections[0];
updateMenuSections(updatedSections);
if (item.id.startsWith("custom")) {
removeCustomData(item.id);
}
if (fallbackSection) {
setActiveSection(fallbackSection.id);
}
}}
className="bg-gradient-to-r from-rose-500 to-orange-400 hover:from-rose-600 hover:to-orange-500 text-white shadow-sm border-0"
>
{t("confirm")}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
</div>
</Reorder.Item>
);
};
export default LayoutItem;