0.5 缩减重构
This commit is contained in:
@@ -1,106 +1,106 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useLayoutEffect, useRef, useState } from "react";
|
||||
import { Copy, Download, Hash, Link as LinkIcon, Move, PenLine, Trash2 } from "lucide-react";
|
||||
import type { MediaAsset } from "@/types/media";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
interface AssetContextMenuProps {
|
||||
asset: MediaAsset;
|
||||
position: { x: number; y: number };
|
||||
onClose: () => void;
|
||||
onOpen: (asset: MediaAsset) => void;
|
||||
onCopyLink: (asset: MediaAsset) => void;
|
||||
onCopyPath: (asset: MediaAsset) => void;
|
||||
onRename: (asset: MediaAsset) => void;
|
||||
onMove: (asset: MediaAsset) => void;
|
||||
onDelete: (assetIds: string[]) => void;
|
||||
onDownload: (asset: MediaAsset) => void;
|
||||
}
|
||||
|
||||
export function AssetContextMenu({
|
||||
asset,
|
||||
position,
|
||||
onClose,
|
||||
onOpen,
|
||||
onCopyLink,
|
||||
onCopyPath,
|
||||
onRename,
|
||||
onMove,
|
||||
onDelete,
|
||||
onDownload,
|
||||
}: AssetContextMenuProps) {
|
||||
const menuRef = useRef<HTMLDivElement>(null);
|
||||
"use client";
|
||||
|
||||
import { useEffect, useLayoutEffect, useRef, useState } from "react";
|
||||
import { Copy, Download, Hash, Link as LinkIcon, Move, PenLine, Trash2 } from "lucide-react";
|
||||
import type { MediaAsset } from "@/types/media";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
interface AssetContextMenuProps {
|
||||
asset: MediaAsset;
|
||||
position: { x: number; y: number };
|
||||
onClose: () => void;
|
||||
onOpen: (asset: MediaAsset) => void;
|
||||
onCopyLink: (asset: MediaAsset) => void;
|
||||
onCopyPath: (asset: MediaAsset) => void;
|
||||
onRename: (asset: MediaAsset) => void;
|
||||
onMove: (asset: MediaAsset) => void;
|
||||
onDelete: (assetIds: string[]) => void;
|
||||
onDownload: (asset: MediaAsset) => void;
|
||||
}
|
||||
|
||||
export function AssetContextMenu({
|
||||
asset,
|
||||
position,
|
||||
onClose,
|
||||
onOpen,
|
||||
onCopyLink,
|
||||
onCopyPath,
|
||||
onRename,
|
||||
onMove,
|
||||
onDelete,
|
||||
onDownload,
|
||||
}: AssetContextMenuProps) {
|
||||
const menuRef = useRef<HTMLDivElement>(null);
|
||||
const [pos, setPos] = useState(position);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const clampPosition = () => {
|
||||
const element = menuRef.current;
|
||||
if (!element) {
|
||||
setPos(position);
|
||||
return;
|
||||
}
|
||||
const rect = element.getBoundingClientRect();
|
||||
const padding = 12;
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const clampPosition = () => {
|
||||
const element = menuRef.current;
|
||||
if (!element) {
|
||||
setPos(position);
|
||||
return;
|
||||
}
|
||||
const rect = element.getBoundingClientRect();
|
||||
const padding = 12;
|
||||
const maxLeft = Math.max(padding, window.innerWidth - rect.width - padding);
|
||||
const maxTop = Math.max(padding, window.innerHeight - rect.height - padding);
|
||||
const left = Math.min(Math.max(padding, position.x), maxLeft);
|
||||
const top = Math.min(Math.max(padding, position.y), maxTop);
|
||||
setPos({ x: left, y: top });
|
||||
};
|
||||
clampPosition();
|
||||
window.addEventListener("resize", clampPosition);
|
||||
return () => window.removeEventListener("resize", clampPosition);
|
||||
}, [position]);
|
||||
|
||||
useEffect(() => {
|
||||
const close = () => onClose();
|
||||
window.addEventListener("click", close);
|
||||
return () => window.removeEventListener("click", close);
|
||||
}, [onClose]);
|
||||
|
||||
const buttonClass =
|
||||
"flex w-full items-center gap-2 rounded-sm px-3 py-2 text-left text-sm text-gray-700 hover:bg-[#f5f7fb]";
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={menuRef}
|
||||
className="fixed z-50 rounded-xl border border-[#ececec] bg-white p-1 text-sm text-gray-700 shadow-2xl"
|
||||
clampPosition();
|
||||
window.addEventListener("resize", clampPosition);
|
||||
return () => window.removeEventListener("resize", clampPosition);
|
||||
}, [position]);
|
||||
|
||||
useEffect(() => {
|
||||
const close = () => onClose();
|
||||
window.addEventListener("click", close);
|
||||
return () => window.removeEventListener("click", close);
|
||||
}, [onClose]);
|
||||
|
||||
const buttonClass =
|
||||
"flex w-full items-center gap-2 rounded-sm px-3 py-2 text-left text-sm text-gray-700 hover:bg-[#f5f7fb]";
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={menuRef}
|
||||
className="fixed z-50 rounded-xl border border-[#ececec] bg-white p-1 text-sm text-gray-700 shadow-2xl"
|
||||
style={{ top: pos.y, left: pos.x, minWidth: 200 }}
|
||||
>
|
||||
<button type="button" className={buttonClass} onClick={() => onOpen(asset)}>
|
||||
<LinkIcon className="h-4 w-4 text-[#2563eb]" />
|
||||
<span>在新标签打开</span>
|
||||
</button>
|
||||
<button type="button" className={buttonClass} onClick={() => onDownload(asset)}>
|
||||
<Download className="h-4 w-4 text-gray-500" />
|
||||
<span>下载</span>
|
||||
</button>
|
||||
<button type="button" className={buttonClass} onClick={() => onCopyLink(asset)}>
|
||||
<Copy className="h-4 w-4 text-gray-500" />
|
||||
<span>复制链接</span>
|
||||
</button>
|
||||
<button type="button" className={buttonClass} onClick={() => onCopyPath(asset)}>
|
||||
<Hash className="h-4 w-4 text-gray-500" />
|
||||
<span>复制存储路径</span>
|
||||
</button>
|
||||
<div className="my-1 border-t border-[#f2f2f2]" />
|
||||
<button type="button" className={buttonClass} onClick={() => onRename(asset)}>
|
||||
<PenLine className="h-4 w-4 text-gray-500" />
|
||||
<span>重命名</span>
|
||||
</button>
|
||||
<button type="button" className={buttonClass} onClick={() => onMove(asset)}>
|
||||
<Move className="h-4 w-4 text-gray-500" />
|
||||
<span>移动到...</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="mt-1 flex w-full items-center gap-2 rounded-sm px-3 py-2 text-left text-sm text-red-500 hover:bg-red-50"
|
||||
onClick={() => onDelete([asset.id])}
|
||||
>
|
||||
<Trash2 className="h-4 w-4" />
|
||||
<span>删除</span>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
>
|
||||
<button type="button" className={buttonClass} onClick={() => onOpen(asset)}>
|
||||
<LinkIcon className="h-4 w-4 text-[#2563eb]" />
|
||||
<span>在新标签打开</span>
|
||||
</button>
|
||||
<button type="button" className={buttonClass} onClick={() => onDownload(asset)}>
|
||||
<Download className="h-4 w-4 text-gray-500" />
|
||||
<span>下载</span>
|
||||
</button>
|
||||
<button type="button" className={buttonClass} onClick={() => onCopyLink(asset)}>
|
||||
<Copy className="h-4 w-4 text-gray-500" />
|
||||
<span>复制链接</span>
|
||||
</button>
|
||||
<button type="button" className={buttonClass} onClick={() => onCopyPath(asset)}>
|
||||
<Hash className="h-4 w-4 text-gray-500" />
|
||||
<span>复制存储路径</span>
|
||||
</button>
|
||||
<div className="my-1 border-t border-[#f2f2f2]" />
|
||||
<button type="button" className={buttonClass} onClick={() => onRename(asset)}>
|
||||
<PenLine className="h-4 w-4 text-gray-500" />
|
||||
<span>重命名</span>
|
||||
</button>
|
||||
<button type="button" className={buttonClass} onClick={() => onMove(asset)}>
|
||||
<Move className="h-4 w-4 text-gray-500" />
|
||||
<span>移动到...</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="mt-1 flex w-full items-center gap-2 rounded-sm px-3 py-2 text-left text-sm text-red-500 hover:bg-red-50"
|
||||
onClick={() => onDelete([asset.id])}
|
||||
>
|
||||
<Trash2 className="h-4 w-4" />
|
||||
<span>删除</span>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import type { DocumentRecord } from "@/lib/documents";
|
||||
import type { DocumentRecord } from "@/lib/documents";
|
||||
import type { WorkspaceSummary } from "@/lib/workspaces";
|
||||
import type { MediaAsset } from "@/types/media";
|
||||
|
||||
export type SidebarSectionId = "starred" | "public" | "shared" | "private" | "templates";
|
||||
|
||||
export interface TrashRecord {
|
||||
id: string;
|
||||
title: string | null;
|
||||
deleted_at: string;
|
||||
parent_id: string | null;
|
||||
access_scope: DocumentRecord["access_scope"];
|
||||
}
|
||||
|
||||
|
||||
export type SidebarSectionId = "starred" | "public" | "shared" | "private" | "templates";
|
||||
|
||||
export interface TrashRecord {
|
||||
id: string;
|
||||
title: string | null;
|
||||
deleted_at: string;
|
||||
parent_id: string | null;
|
||||
access_scope: DocumentRecord["access_scope"];
|
||||
}
|
||||
|
||||
export interface SidebarInitialData {
|
||||
activeWorkspaceId: string;
|
||||
workspaces: WorkspaceSummary[];
|
||||
|
||||
Reference in New Issue
Block a user