📘
【Convex】NextJs14 と Convex【#27 Convex Rename Card Modal】
【#27 Convex Rename Card Modal】
YouTube: https://youtu.be/JxmCBG1Z1e4
今回から何回かに分けて
カードタイトルをリネームするモーダルを実装します。
今回はモーダルを開くところまでの実装となります。
store/use-rename-card-modal.ts
import { create } from "zustand";
const defaultValues = { id: "", title: "" };
interface RenameCardModalInput {
isOpen: boolean;
initialValues: typeof defaultValues;
onOpen: (id: string, title: string) => void;
onClose: () => void;
}
export const useRenameCardModal = create<RenameCardModalInput>((set) => ({
isOpen: false,
onOpen: (id, title) => set({ isOpen: true, initialValues: { id, title } }),
onClose: () => set({ isOpen: false, initialValues: defaultValues }),
initialValues: defaultValues,
}));
components/modals/rename-card-modal.tsx
"use client";
import {
Dialog,
DialogContent,
DialogHeader,
DialogDescription,
DialogClose,
DialogFooter,
DialogTitle,
} from "@/components/ui/dialog";
import { useRenameCardModal } from "@/store/use-rename-card-modal";
export const RenameCardModal = () => {
const { isOpen, onClose, initialValues } = useRenameCardModal();
return (
<Dialog open={isOpen} onOpenChange={onClose}>
<DialogContent>
<DialogHeader>
<DialogTitle>Rename card title</DialogTitle>
<DialogDescription>Enter a new card title.</DialogDescription>
</DialogHeader>
</DialogContent>
</Dialog>
);
};
components/providers/modal-provider.tsx
"use client";
import { useEffect, useState } from "react";
import { CreateCardModal } from "@/components/modals/create-card-modal";
import { RenameCardModal } from "../modals/rename-card-modal";
export const ModalProvider = () => {
const [isMounted, setIsMounted] = useState(false);
useEffect(() => {
setIsMounted(true);
}, []);
if (!isMounted) {
return null;
}
return (
<>
<RenameCardModal />
<CreateCardModal />
</>
);
};
app/(main)/_components/card/card-actions.tsx
"use client";
import { Pencil, Trash2 } from "lucide-react";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { useRenameCardModal } from "@/store/use-rename-card-modal";
interface Props {
children: React.ReactNode;
id: string;
title: string;
}
export const CardActions = ({ children, id, title }: Props) => {
const { onOpen } = useRenameCardModal();
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>{children}</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem
className="p-3 cursor-pointer"
onClick={() => onOpen(id, title)}
>
<Pencil className="h-4 w-4 mr-2" /> Rename
</DropdownMenuItem>
<DropdownMenuItem className="p-3 cursor-pointer">
<Trash2 className="h-4 w-4 mr-2" /> Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
};
app/(main)/_components/card/index.tsx
"use client";
import Image from "next/image";
import { useAuth } from "@clerk/nextjs";
import { formatDistanceToNow } from "date-fns";
import { Footer } from "./footer";
import { Overlay } from "./overlay";
import { CardActions } from "./card-actions";
import { MoreHorizontal } from "lucide-react";
interface Props {
id: string;
title: string;
imageUrl: string;
authorId: string;
authorName: string;
createdAt: number;
orgId: string;
cardType: string;
}
export const Card = ({
id,
title,
imageUrl,
authorId,
authorName,
createdAt,
orgId,
cardType,
}: Props) => {
const { userId } = useAuth();
const authorLabel = userId === authorId ? "You" : authorName;
const createdAtLabel = formatDistanceToNow(createdAt, { addSuffix: true });
return (
<div className="group aspect-[100/128] border rounded-lg flex flex-col justify-between overflow-hidden">
<div className="relative flex-1 bg-slate-300">
<Image src={imageUrl} alt={title} fill className="object-cover" />
<Overlay />
<CardActions id={id} title={title}>
<button className="absolute top-1 right-1 opacity-0 group-hover:opacity-100 transition-opacity px-3 py-2 outline-none">
<MoreHorizontal className="text-white opacity-75 hover:opacity-100 transition-opacity" />
</button>
</CardActions>
</div>
<Footer
title={title}
authorLabel={authorLabel}
type={cardType}
createdAtLabel={createdAtLabel}
/>
</div>
);
};
Discussion