🎃

【Convex】NextJs14 と Convex【#26 Convex Card Actions】

2024/05/23に公開

【#26 Convex Card Actions】

YouTube: https://youtu.be/UOvg9ldY-OQ
https://youtu.be/UOvg9ldY-OQ

今回はカードのアップデートとデリートの
メニューを実装します。

npx shadcn-ui@latest add dropdown-menu
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>
          <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>
  );
};
app/(main)/_components/card/overlay.tsx
export const Overlay = () => {
  return (
    <div className="opacity-0 group-hover:opacity-50 transition-opacity h-full w-full bg-black" />
  );
};
app/(main)/_components/card/card-actions.tsx
"use client";

import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Pencil, Trash2 } from "lucide-react";

interface Props {
  children: React.ReactNode;
}

export const CardActions = ({ children }: Props) => {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger asChild>{children}</DropdownMenuTrigger>
      <DropdownMenuContent>
        <DropdownMenuItem className="p-3 cursor-pointer">
          <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>
  );
};

Discussion