0.5 缩减重构

This commit is contained in:
lix-2026
2026-04-13 19:21:42 +08:00
parent af92c4b149
commit 71fb1aee7e
2023 changed files with 21113 additions and 394493 deletions
@@ -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>
);
}
+12 -12
View File
@@ -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[];