diff options
Diffstat (limited to 'apps/web/components/dashboard/bookmarks/BookmarkLayoutAdaptingCard.tsx')
| -rw-r--r-- | apps/web/components/dashboard/bookmarks/BookmarkLayoutAdaptingCard.tsx | 83 |
1 files changed, 80 insertions, 3 deletions
diff --git a/apps/web/components/dashboard/bookmarks/BookmarkLayoutAdaptingCard.tsx b/apps/web/components/dashboard/bookmarks/BookmarkLayoutAdaptingCard.tsx index bf7fe2ad..f164b275 100644 --- a/apps/web/components/dashboard/bookmarks/BookmarkLayoutAdaptingCard.tsx +++ b/apps/web/components/dashboard/bookmarks/BookmarkLayoutAdaptingCard.tsx @@ -2,10 +2,11 @@ import type { BookmarksLayoutTypes } from "@/lib/userLocalSettings/types"; import type { ReactNode } from "react"; -import { useEffect, useState } from "react"; +import { useCallback, useEffect, useState } from "react"; import Image from "next/image"; import Link from "next/link"; import { useSession } from "@/lib/auth/client"; +import { BOOKMARK_DRAG_MIME } from "@/lib/bookmark-drag"; import useBulkActionsStore from "@/lib/bulkActions"; import { bookmarkLayoutSwitch, @@ -14,14 +15,22 @@ import { } from "@/lib/userLocalSettings/bookmarksLayout"; import { cn } from "@/lib/utils"; import { useQuery } from "@tanstack/react-query"; -import { Check, Image as ImageIcon, NotebookPen } from "lucide-react"; +import { + Check, + GripVertical, + Image as ImageIcon, + NotebookPen, +} from "lucide-react"; import { useTheme } from "next-themes"; import type { ZBookmark } from "@karakeep/shared/types/bookmarks"; import { useBookmarkListContext } from "@karakeep/shared-react/hooks/bookmark-list-context"; import { useTRPC } from "@karakeep/shared-react/trpc"; import { BookmarkTypes } from "@karakeep/shared/types/bookmarks"; -import { isBookmarkStillTagging } from "@karakeep/shared/utils/bookmarkUtils"; +import { + getBookmarkTitle, + isBookmarkStillTagging, +} from "@karakeep/shared/utils/bookmarkUtils"; import { switchCase } from "@karakeep/shared/utils/switch"; import BookmarkActionBar from "./BookmarkActionBar"; @@ -155,6 +164,65 @@ function MultiBookmarkSelector({ bookmark }: { bookmark: ZBookmark }) { ); } +function DragHandle({ + bookmark, + className, +}: { + bookmark: ZBookmark; + className?: string; +}) { + const { isBulkEditEnabled } = useBulkActionsStore(); + const handleDragStart = useCallback( + (e: React.DragEvent) => { + e.stopPropagation(); + e.dataTransfer.setData(BOOKMARK_DRAG_MIME, bookmark.id); + e.dataTransfer.effectAllowed = "copy"; + + // Create a small pill element as the drag preview + const pill = document.createElement("div"); + const title = getBookmarkTitle(bookmark) ?? "Untitled"; + pill.textContent = + title.length > 40 ? title.substring(0, 40) + "\u2026" : title; + Object.assign(pill.style, { + position: "fixed", + left: "-9999px", + top: "-9999px", + padding: "6px 12px", + borderRadius: "8px", + backgroundColor: "hsl(var(--card))", + border: "1px solid hsl(var(--border))", + boxShadow: "0 4px 12px rgba(0,0,0,0.15)", + fontSize: "13px", + fontFamily: "inherit", + color: "hsl(var(--foreground))", + maxWidth: "240px", + whiteSpace: "nowrap", + overflow: "hidden", + textOverflow: "ellipsis", + }); + document.body.appendChild(pill); + e.dataTransfer.setDragImage(pill, 0, 0); + requestAnimationFrame(() => pill.remove()); + }, + [bookmark], + ); + + if (isBulkEditEnabled) return null; + + return ( + <div + draggable + onDragStart={handleDragStart} + className={cn( + "absolute z-40 cursor-grab rounded bg-background/70 p-0.5 opacity-0 shadow-sm transition-opacity duration-200 group-hover:opacity-100", + className, + )} + > + <GripVertical className="size-4 text-muted-foreground" /> + </div> + ); +} + function ListView({ bookmark, image, @@ -180,6 +248,10 @@ function ListView({ > <MultiBookmarkSelector bookmark={bookmark} /> <OwnerIndicator bookmark={bookmark} /> + <DragHandle + bookmark={bookmark} + className="left-1 top-1/2 -translate-y-1/2" + /> <div className="flex size-32 items-center justify-center overflow-hidden"> {image("list", cn("size-32 rounded-lg", imgFitClass))} </div> @@ -240,6 +312,7 @@ function GridView({ > <MultiBookmarkSelector bookmark={bookmark} /> <OwnerIndicator bookmark={bookmark} /> + <DragHandle bookmark={bookmark} className="left-2 top-2" /> {img && <div className="h-56 w-full shrink-0 overflow-hidden">{img}</div>} <div className="flex h-full flex-col justify-between gap-2 overflow-hidden p-2"> <div className="grow-1 flex flex-col gap-2 overflow-hidden"> @@ -278,6 +351,10 @@ function CompactView({ bookmark, title, footer, className }: Props) { > <MultiBookmarkSelector bookmark={bookmark} /> <OwnerIndicator bookmark={bookmark} /> + <DragHandle + bookmark={bookmark} + className="left-0.5 top-1/2 -translate-y-1/2" + /> <div className="flex h-full justify-between gap-2 overflow-hidden p-2"> <div className="flex items-center gap-2"> {bookmark.content.type === BookmarkTypes.LINK && |
