This commit is contained in:
Maze Winther
2026-03-02 17:33:01 +01:00
+127 -55
View File
@@ -46,6 +46,13 @@ import {
} from "@hugeicons/core-free-icons";
import { OcVideoIcon } from "@opencut/ui/icons";
import { Label } from "@/components/ui/label";
import {
ContextMenu,
ContextMenuContent,
ContextMenuItem,
ContextMenuSeparator,
ContextMenuTrigger,
} from "@/components/ui/context-menu";
import {
DropdownMenu,
DropdownMenuCheckboxItem,
@@ -534,10 +541,25 @@ function ProjectItem({
const isSelected = selectedProjectIdSet.has(project.id);
const selectedProjectCount = selectedProjectIds.length;
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
const [isRenameDialogOpen, setIsRenameDialogOpen] = useState(false);
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
const [isInfoDialogOpen, setIsInfoDialogOpen] = useState(false);
const editor = useEditor();
const durationLabel = formatProjectDuration({ duration: project.duration });
const isMultiSelect = selectedProjectCount > 1;
const isGridView = viewMode === "grid";
const handleRename = () => setIsRenameDialogOpen(true);
const handleDuplicate = async () => {
await duplicateProjects({ editor, ids: [project.id] });
};
const handleDeleteClick = () => setIsDeleteDialogOpen(true);
const handleInfoClick = () => setIsInfoDialogOpen(true);
const handleDeleteConfirm = async () => {
await deleteProjects({ editor, ids: [project.id] });
setIsDeleteDialogOpen(false);
};
const handleCheckboxChange = ({
checked,
shiftKey,
@@ -622,8 +644,7 @@ function ProjectItem({
const listContent = (
<div
className={`flex items-center gap-4 py-2 px-4 border-b border-border/50 ${
isSelected ? "bg-primary/5" : ""
className={`flex items-center gap-4 py-2 px-4 border-b border-border/50 ${isSelected ? "bg-primary/5" : ""
}`}
>
<Checkbox
@@ -647,27 +668,27 @@ function ProjectItem({
<ProjectMenu
isOpen={isDropdownOpen}
onOpenChange={setIsDropdownOpen}
project={project}
variant="list"
onRenameClick={handleRename}
onDuplicateClick={handleDuplicate}
onDeleteClick={handleDeleteClick}
onInfoClick={handleInfoClick}
/>
)}
</div>
);
const cardContent = isGridView ? gridContent : listContent;
if (!isGridView) {
return <div className="group relative">{listContent}</div>;
}
return (
<>
<ContextMenu>
<ContextMenuTrigger asChild>
<div className="group relative">
{isGridView ? (
<>
<Link href={`/editor/${project.id}`} className="block">
{cardContent}
{gridContent}
</Link>
{isGridView && (
<>
<Checkbox
checked={isSelected}
onMouseDown={(event) => event.preventDefault()}
@@ -689,31 +710,112 @@ function ProjectItem({
<ProjectMenu
isOpen={isDropdownOpen}
onOpenChange={setIsDropdownOpen}
project={project}
onRenameClick={handleRename}
onDuplicateClick={handleDuplicate}
onDeleteClick={handleDeleteClick}
onInfoClick={handleInfoClick}
/>
)}
</>
) : (
listContent
)}
</div>
</ContextMenuTrigger>
<ProjectContextMenuContent
onRenameClick={handleRename}
onDuplicateClick={handleDuplicate}
onDeleteClick={handleDeleteClick}
onInfoClick={handleInfoClick}
/>
</ContextMenu>
<RenameProjectDialog
isOpen={isRenameDialogOpen}
onOpenChange={setIsRenameDialogOpen}
projectName={project.name}
onConfirm={async (newName) => {
await renameProject({ editor, id: project.id, name: newName });
setIsRenameDialogOpen(false);
}}
/>
<DeleteProjectDialog
isOpen={isDeleteDialogOpen}
onOpenChange={setIsDeleteDialogOpen}
projectNames={[project.name]}
onConfirm={handleDeleteConfirm}
/>
<ProjectInfoDialog
isOpen={isInfoDialogOpen}
onOpenChange={setIsInfoDialogOpen}
project={project}
/>
</>
);
}
function ProjectContextMenuContent({
onRenameClick,
onDuplicateClick,
onDeleteClick,
onInfoClick,
}: {
onRenameClick: () => void;
onDuplicateClick: () => void;
onDeleteClick: () => void;
onInfoClick: () => void;
}) {
return (
<ContextMenuContent>
<ContextMenuItem
icon={<HugeiconsIcon icon={Edit03Icon} />}
onClick={onRenameClick}
>
Rename
</ContextMenuItem>
<ContextMenuItem
icon={<HugeiconsIcon icon={Copy01Icon} />}
onClick={onDuplicateClick}
>
Duplicate
</ContextMenuItem>
<ContextMenuItem
icon={<HugeiconsIcon icon={InformationCircleIcon} />}
onClick={onInfoClick}
>
Info
</ContextMenuItem>
<ContextMenuSeparator />
<ContextMenuItem
variant="destructive"
icon={<HugeiconsIcon icon={Delete02Icon} />}
onClick={onDeleteClick}
>
Delete
</ContextMenuItem>
</ContextMenuContent>
);
}
function ProjectMenu({
isOpen,
onOpenChange,
project,
variant = "grid",
onRenameClick,
onDuplicateClick,
onDeleteClick,
onInfoClick,
}: {
isOpen: boolean;
onOpenChange: (open: boolean) => void;
project: TProjectMetadata;
variant?: "grid" | "list";
onRenameClick: () => void;
onDuplicateClick: () => void;
onDeleteClick: () => void;
onInfoClick: () => void;
}) {
const editor = useEditor();
const [isRenameDialogOpen, setIsRenameDialogOpen] = useState(false);
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
const [isInfoDialogOpen, setIsInfoDialogOpen] = useState(false);
const handleMenuClick = ({
event,
}: {
@@ -736,34 +838,28 @@ function ProjectMenu({
};
const handleRename = () => {
setIsRenameDialogOpen(true);
onRenameClick();
onOpenChange(false);
};
const handleDuplicate = async () => {
await duplicateProjects({ editor, ids: [project.id] });
const handleDuplicate = () => {
onDuplicateClick();
onOpenChange(false);
};
const handleDeleteClick = () => {
setIsDeleteDialogOpen(true);
onDeleteClick();
onOpenChange(false);
};
const handleDeleteConfirm = async () => {
await deleteProjects({ editor, ids: [project.id] });
setIsDeleteDialogOpen(false);
};
const handleInfoClick = () => {
setIsInfoDialogOpen(true);
onInfoClick();
onOpenChange(false);
};
const isGrid = variant === "grid";
return (
<>
<DropdownMenu open={isOpen} onOpenChange={onOpenChange}>
<DropdownMenuTrigger asChild>
<Button
@@ -813,30 +909,6 @@ function ProjectMenu({
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<RenameProjectDialog
isOpen={isRenameDialogOpen}
onOpenChange={setIsRenameDialogOpen}
projectName={project.name}
onConfirm={async (newName) => {
await renameProject({ editor, id: project.id, name: newName });
setIsRenameDialogOpen(false);
}}
/>
<DeleteProjectDialog
isOpen={isDeleteDialogOpen}
onOpenChange={setIsDeleteDialogOpen}
projectNames={[project.name]}
onConfirm={handleDeleteConfirm}
/>
<ProjectInfoDialog
isOpen={isInfoDialogOpen}
onOpenChange={setIsInfoDialogOpen}
project={project}
/>
</>
);
}