From 3f4d850815dcc33ed1506ead14c022b0d086d726 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 12 Apr 2026 00:23:45 +0800 Subject: [PATCH] refactor: share floating sidebar shell --- components/flow-board/drawnix-workspace.tsx | 231 +++++--------------- components/home/floating-sidebar-shell.tsx | 142 ++++++++++++ components/home/lovart-home.tsx | 5 +- 3 files changed, 200 insertions(+), 178 deletions(-) create mode 100644 components/home/floating-sidebar-shell.tsx diff --git a/components/flow-board/drawnix-workspace.tsx b/components/flow-board/drawnix-workspace.tsx index 9e9c2a165..96ca8d090 100644 --- a/components/flow-board/drawnix-workspace.tsx +++ b/components/flow-board/drawnix-workspace.tsx @@ -28,6 +28,12 @@ import { parseMindMapFileToDrawnixMind, } from "@/lib/flow-board/mind-map-import"; import { fetchUploadWithRetry } from "@/lib/client/upload-with-retry"; +import { + FloatingLogoPill, + FloatingSidebarAction, + FloatingSidebarRail, + FloatingSidebarShell, +} from "@/components/home/floating-sidebar-shell"; import { CheckCircle2, ChevronDown, @@ -46,12 +52,6 @@ import { X, } from "lucide-react"; import { useTheme } from "next-themes"; -import { - Tooltip, - TooltipContent, - TooltipProvider, - TooltipTrigger, -} from "@/components/ui/tooltip"; import { type ChangeEvent, type ClipboardEvent, @@ -121,10 +121,6 @@ const FLOW_BOARD_IMAGE_UPLOAD_TYPES = new Set([ "image/png", "image/webp", ]); -const FLOW_BOARD_SIDEBAR_BUTTON_CLASS = - "flex h-10 w-10 items-center justify-center rounded-full text-[#4f566b] transition-colors hover:bg-black/5 hover:text-[#111827] disabled:cursor-not-allowed disabled:opacity-45"; -const FLOW_BOARD_SIDEBAR_BUTTON_ACTIVE_CLASS = - "bg-[#111827] text-white shadow-sm hover:bg-[#1f2937] hover:text-white"; const FLOW_BOARD_MARKER_GROUPS: FlowBoardMarkerGroup[] = [ { id: "tag", @@ -375,16 +371,6 @@ function normalizeSnapshot(value: unknown): FlowBoardSnapshot { }; } -function formatSavedAt(value: Date | null) { - if (!value) { - return "数据库自动保存"; - } - return `已保存 ${value.toLocaleTimeString("zh-CN", { - hour: "2-digit", - minute: "2-digit", - })}`; -} - function formatFileUpdatedAt(value?: string | null) { if (!value) return "刚刚"; const date = new Date(value); @@ -564,7 +550,7 @@ export function DrawnixWorkspace({ const [isFilePanelOpen, setIsFilePanelOpen] = useState(false); const [isFileListLoading, setIsFileListLoading] = useState(false); const [isSavingNow, setIsSavingNow] = useState(false); - const [lastSavedAt, setLastSavedAt] = useState(null); + const [, setLastSavedAt] = useState(null); const snapshotRef = useRef({ children: [] }); const currentFileIdRef = useRef(null); const currentTitleRef = useRef("流程白板"); @@ -1328,15 +1314,6 @@ export function DrawnixWorkspace({ [cloudFiles, hydrateCloudFile, updateBrowserFileParam] ); - const handleTitleCommit = useCallback(async () => { - const normalizedTitle = currentTitle.trim() || "未命名流程白板"; - if (normalizedTitle !== currentTitle) { - setCurrentTitle(normalizedTitle); - currentTitleRef.current = normalizedTitle; - } - await flushPendingSave(false); - }, [currentTitle, flushPendingSave]); - if (!isReady) { return (
@@ -1383,154 +1360,56 @@ export function DrawnixWorkspace({ ref={mindMapFileInputRef} type="file" /> - + + flushPendingSave(true)} + /> + + mindMapFileInputRef.current?.click()} + /> + { + setIsFilePanelOpen(false); + setIsMarkerPanelOpen((open) => !open); + refreshMarkerSelectionState(); + }} + /> + + + {isFilePanelOpen ? ( + ); +} + +type FloatingLogoPillProps = { + siteName: string; + siteLogo?: string | null; + tooltip?: string; + onClick?: () => void; +}; + +export function FloatingLogoPill({ + siteName, + siteLogo, + tooltip = siteName, + onClick, +}: FloatingLogoPillProps) { + return ( + + + + + {tooltip} + + ); +} + +type FloatingSidebarRailProps = { + children: ReactNode; + className?: string; +}; + +export function FloatingSidebarRail({ + children, + className, +}: FloatingSidebarRailProps) { + return ( +
+ {children} +
+ ); +} + +type FloatingSidebarActionProps = { + label: string; + icon: LucideIcon; + active?: boolean; + disabled?: boolean; + loading?: boolean; + onClick?: () => void; +}; + +export function FloatingSidebarAction({ + label, + icon: Icon, + active = false, + disabled = false, + loading = false, + onClick, +}: FloatingSidebarActionProps) { + return ( + + + + + {label} + + ); +} diff --git a/components/home/lovart-home.tsx b/components/home/lovart-home.tsx index 687f6458e..2ed4c8047 100644 --- a/components/home/lovart-home.tsx +++ b/components/home/lovart-home.tsx @@ -56,6 +56,7 @@ import { import type { MembershipConfig } from "@/lib/membership/config"; import { ImageAnnotator } from "@/components/image-annotator"; import { HomeAnimationEmbed } from "@/components/home/home-animation-embed"; +import { FloatingSidebarRail } from "@/components/home/floating-sidebar-shell"; import { fetchUploadWithRetry } from "@/lib/client/upload-with-retry"; type HomeUser = { @@ -1432,7 +1433,7 @@ export function LovartHome({ 新建项目 -
+
+