refactor: 统一图表保存状态样式

This commit is contained in:
tianma-if committed 2026-09-06 17:13:48 +08:00
1 parent 7f84ee4349
commit cece99d7ed
2 files changed
+53 -11

No files matched your search

@@ -59,6 +59,11 @@ describe("diagram editor canvas surface", () => {
expect(source).toContain("!readOnly && saveFailed");
expect(source).toContain('t("diagram.retrySave")');
expect(source).not.toContain('<Save className="h-4 w-4" />');
expect(source).toContain('t("editor.saveState.unsaved")');
expect(source).toContain('"bg-slate-100 text-slate-500"');
expect(source).toContain("{...statusSettleMotion}");
expect(source).toContain('saveStatus === "unsaved" ? (');
expect(source).not.toContain('t("diagram.saved")');
});
test("uses a clean grid-free canvas for both diagram types", () => {
+48 -11
View File
@@ -1,5 +1,6 @@
import { useCallback, useEffect, useRef, useState } from "react";
import { Export, Graph, History, Keyboard, Selection, type Edge, type Node } from "@antv/x6";
import * as m from "motion/react-m";
import {
Activity,
AppWindow,
@@ -105,6 +106,7 @@ import { compactArchitectureNodeSize, compactFlowchartNodeSize, compactMindMapNo
import { resolveDiagramPalette, type DiagramAppearance } from "@/lib/diagram-theme";
import { isLocalMemoId } from "@/lib/local-mirror";
import { isBrowserOffline } from "@/lib/network-status";
import { statusSettleMotion } from "@/lib/motion";
import type { EdgeEverRepository } from "@/lib/repository";
import { cn, formatDateTime } from "@/lib/utils";
@@ -1938,6 +1940,19 @@ export const DiagramEditorPane = ({
const currentMarkdown = historyOpen
? serializeDiagramDocument(graphRef.current ? graphToDocument(graphRef.current, document.kind, themeRef.current) : document)
: memo.contentMarkdown;
const saveStatus = saveError ? "error" : saving ? "saving" : dirty ? "unsaved" : "saved";
const saveLabel = saveStatus === "error"
? t("editor.saveState.error")
: saveStatus === "saving"
? t("editor.saveState.saving")
: saveStatus === "unsaved"
? t("editor.saveState.unsaved")
: t("editor.saveState.saved");
const saveStatusClassName = saveStatus === "error"
? "bg-rose-50 text-rose-700"
: saveStatus === "saved"
? "bg-slate-100 text-slate-500"
: "bg-emerald-50 text-emerald-700";
return (
<TooltipProvider>
@@ -2001,19 +2016,41 @@ export const DiagramEditorPane = ({
</div>
<div className="flex shrink-0 items-center gap-1">
<span className={cn(
<m.span
key={`mobile-${saveStatus}`}
className={cn(
"inline-flex max-w-[5.5rem] truncate rounded-full px-2 py-1 text-[11px] font-medium sm:hidden",
saveError ? "bg-rose-50 text-rose-700" : dirty || saving ? "bg-amber-50 text-amber-700" : "bg-emerald-50 text-emerald-700",
)} role="status" aria-live="polite">
{saveError ?? (dirty || saving ? t("diagram.saving") : t("diagram.saved"))}
</span>
<span className={cn(
saveStatusClassName,
)}
role="status"
aria-live="polite"
aria-label={saveError ? `${saveLabel}. ${saveError}` : undefined}
{...statusSettleMotion}
>
{saveLabel}
</m.span>
<m.span
key={saveStatus}
className={cn(
"hidden items-center gap-1.5 rounded-md px-2 py-1 text-xs font-medium sm:inline-flex",
saveError ? "bg-rose-50 text-rose-700" : dirty || saving ? "bg-amber-50 text-amber-700" : "bg-emerald-50 text-emerald-700",
)} role="status" aria-live="polite">
{saveError ? <CircleAlert className="h-3 w-3" /> : dirty || saving ? <LoaderCircle className="h-3 w-3 animate-spin" /> : <Check className="h-3 w-3" />}
{saveError ?? (dirty || saving ? t("diagram.saving") : t("diagram.saved"))}
</span>
saveStatusClassName,
)}
role="status"
aria-live="polite"
aria-label={saveError ? `${saveLabel}. ${saveError}` : undefined}
{...statusSettleMotion}
>
{saveStatus === "error" ? (
<CircleAlert className="h-3 w-3" aria-hidden="true" />
) : saveStatus === "saving" ? (
<LoaderCircle className="h-3 w-3 animate-spin" aria-hidden="true" />
) : saveStatus === "unsaved" ? (
<Pencil className="h-3 w-3" aria-hidden="true" />
) : (
<Check className="h-3 w-3" aria-hidden="true" />
)}
{saveLabel}
</m.span>
{!readOnly && saveFailed && (
<Button variant="soft" size="sm" disabled={saving || !editSessionReady} onClick={() => void save()}>
<RefreshCw className="h-4 w-4" />