refactor(web): unify compact editor chrome

This commit is contained in:
tianma-if committed 2026-09-07 13:24:19 +08:00
1 parent d032072c67
commit 766faacbcf
7 files changed
+61 -17

No files matched your search

@@ -256,6 +256,12 @@ describe("diagram editor canvas surface", () => {
expect(source).toContain('graph.startBatch("quick-create")');
});
test("keeps the desktop header compact without shrinking mobile controls", () => {
expect(source).toContain("MEMO_EDITOR_TOP_ROW_CLASS_NAME");
expect(source).toContain("MEMO_EDITOR_TITLE_ROW_CLASS_NAME");
expect(toolbarSource).toContain("MEMO_EDITOR_TOOLBAR_PADDING_CLASS_NAME");
});
test("repaints the graph when the application appearance changes", () => {
expect(source).toContain("const { resolvedTheme } = useAppearanceTheme();");
expect(source).toContain("applyGraphPalette(graph, themeRef.current, document.kind, resolvedTheme);");
@@ -90,6 +90,10 @@ import { ShareMemoDialog } from "@/components/dialogs/ShareMemoDialog";
import { ClipboardCopyNotice } from "@/components/ClipboardCopyNotice";
import { DiagramToolbar, DiagramToolbarAddTrigger } from "@/components/DiagramToolbar";
import { MemoEditorHeaderActions } from "@/components/MemoEditorHeaderActions";
import {
MEMO_EDITOR_TITLE_ROW_CLASS_NAME,
MEMO_EDITOR_TOP_ROW_CLASS_NAME,
} from "@/components/MemoEditorChromeDensity";
import { EditorNoteSearchBar } from "@/components/editor/EditorNoteSearchBar";
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
@@ -2180,7 +2184,7 @@ export const DiagramEditorPane = ({
<TooltipProvider>
<div className="flex h-full min-h-0 flex-col bg-white">
<header className="shrink-0 border-b border-slate-200 bg-white">
<div className="flex min-h-12 items-center justify-between gap-2 border-b border-slate-100 px-3 py-2 sm:px-5">
<div className={MEMO_EDITOR_TOP_ROW_CLASS_NAME}>
<div className="flex min-w-0 items-center gap-2 text-sm">
<Tooltip>
<TooltipTrigger asChild>
@@ -2340,7 +2344,7 @@ export const DiagramEditorPane = ({
</div>
</div>
<div className="flex min-h-14 items-center gap-3 px-4 py-2.5 sm:px-7">
<div className={MEMO_EDITOR_TITLE_ROW_CLASS_NAME}>
<MemoTitleInput
value={title}
readOnly={readOnly}
+3 -1
View File
@@ -17,6 +17,7 @@ import {
import { useTranslation } from "react-i18next";
import type { DiagramTheme } from "@edgeever/shared";
import { Button } from "@/components/ui/button";
import { MEMO_EDITOR_TOOLBAR_PADDING_CLASS_NAME } from "@/components/MemoEditorChromeDensity";
import {
DropdownMenu,
DropdownMenuContent,
@@ -26,6 +27,7 @@ import {
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { resolveDiagramPalette, type DiagramAppearance } from "@/lib/diagram-theme";
import { cn } from "@/lib/utils";
type DiagramToolbarProps = {
appearance: DiagramAppearance;
@@ -93,7 +95,7 @@ export const DiagramToolbar = ({
}: DiagramToolbarProps) => {
const { t } = useTranslation();
return (
<div className="flex shrink-0 flex-wrap items-center gap-1 border-b border-slate-200 bg-white px-3 py-2" role="toolbar" aria-label={t("diagram.toolbar")}>
<div className={cn("flex shrink-0 flex-wrap items-center gap-1 border-b border-slate-200 bg-white", MEMO_EDITOR_TOOLBAR_PADDING_CLASS_NAME)} role="toolbar" aria-label={t("diagram.toolbar")}>
{leading ? <>{leading}<ToolbarDivider /></> : null}
<Tooltip><TooltipTrigger asChild><Button size="icon" variant="ghost" aria-label={t("diagram.undo")} disabled={!canUndo || readOnly} onClick={onUndo}><Undo2 className="h-4 w-4" /></Button></TooltipTrigger><TooltipContent>{t("diagram.undo")}</TooltipContent></Tooltip>
<Tooltip><TooltipTrigger asChild><Button size="icon" variant="ghost" aria-label={t("diagram.redo")} disabled={!canRedo || readOnly} onClick={onRedo}><Redo2 className="h-4 w-4" /></Button></TooltipTrigger><TooltipContent>{t("diagram.redo")}</TooltipContent></Tooltip>
+19 -13
View File
@@ -41,6 +41,10 @@ import {
import { Button } from "@/components/ui/button";
import { ClipboardCopyNotice } from "@/components/ClipboardCopyNotice";
import { MemoEditorHeaderActions } from "@/components/MemoEditorHeaderActions";
import {
MEMO_EDITOR_TEXT_TITLE_REGION_CLASS_NAME,
MEMO_EDITOR_TOP_ROW_CLASS_NAME,
} from "@/components/MemoEditorChromeDensity";
import { MemoTitleInput } from "@/components/MemoTitleInput";
import { Input } from "@/components/ui/input";
import {
@@ -3807,7 +3811,7 @@ const RichEditorPane = ({
/>
)}
<header className="shrink-0 border-b border-slate-200 bg-white">
<div className="flex min-h-12 items-center justify-between gap-2 border-b border-slate-100 px-3 py-2 sm:px-5">
<div className={MEMO_EDITOR_TOP_ROW_CLASS_NAME}>
<div className="flex min-w-0 items-center gap-2 text-sm">
<Button
className="lg:hidden"
@@ -4146,18 +4150,20 @@ const RichEditorPane = ({
</div>
</div>
<div className="space-y-1.5 px-4 pb-2.5 pt-2.5 sm:space-y-3 sm:px-7 sm:pb-4 sm:pt-4 lg:space-y-0 lg:pb-0.5 lg:pt-1.5">
<MemoTitleInput
value={title}
readOnly={effectiveReadOnly}
onValueChange={(nextTitle) => {
setTitle(nextTitle);
persistCurrentDraft(nextTitle, tagsText, getMobilePlainTextValue());
markDirty();
}}
placeholder={t("common.untitledMemo")}
/>
<div className="flex flex-wrap items-center gap-1.5 sm:gap-2">
<div className={MEMO_EDITOR_TEXT_TITLE_REGION_CLASS_NAME}>
<div className="min-w-0 min-[1600px]:flex-1">
<MemoTitleInput
value={title}
readOnly={effectiveReadOnly}
onValueChange={(nextTitle) => {
setTitle(nextTitle);
persistCurrentDraft(nextTitle, tagsText, getMobilePlainTextValue());
markDirty();
}}
placeholder={t("common.untitledMemo")}
/>
</div>
<div className="flex flex-wrap items-center gap-1.5 sm:gap-2 min-[1600px]:shrink-0 min-[1600px]:flex-nowrap">
<button
className="flex h-7 min-w-0 max-w-full items-center gap-1 rounded-md border border-transparent bg-transparent px-1.5 text-xs font-medium text-slate-600 outline-none transition hover:border-slate-200 hover:bg-slate-50 hover:text-slate-900 focus-visible:border-emerald-300 focus-visible:ring-2 focus-visible:ring-emerald-500/20 disabled:opacity-50 sm:hidden"
type="button"
+3 -1
View File
@@ -22,6 +22,7 @@ import {
ChevronUp,
} from "lucide-react";
import { cn } from "@/lib/utils";
import { MEMO_EDITOR_TOOLBAR_PADDING_CLASS_NAME } from "@/components/MemoEditorChromeDensity";
import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from "@/components/ui/select";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
import {
@@ -288,7 +289,8 @@ export const EditorToolbar = ({
<div
ref={controlsRef}
className={cn(
"flex min-w-0 max-w-full flex-wrap items-center gap-1 px-3 py-2 sm:px-5",
"flex min-w-0 max-w-full flex-wrap items-center gap-1",
MEMO_EDITOR_TOOLBAR_PADDING_CLASS_NAME,
hasOverflow && "pr-14 sm:pr-16",
!expanded && "max-h-12 overflow-hidden"
)}
@@ -0,0 +1,11 @@
export const MEMO_EDITOR_TOP_ROW_CLASS_NAME =
"flex min-h-12 items-center justify-between gap-2 border-b border-slate-100 px-3 py-2 sm:min-h-10 sm:px-5 sm:py-1";
export const MEMO_EDITOR_TITLE_ROW_CLASS_NAME =
"flex min-h-12 items-center gap-3 px-4 py-2 sm:min-h-11 sm:px-7 sm:py-1";
export const MEMO_EDITOR_TOOLBAR_PADDING_CLASS_NAME =
"px-3 py-2 sm:px-5 sm:py-1";
export const MEMO_EDITOR_TEXT_TITLE_REGION_CLASS_NAME =
"space-y-1.5 px-4 pb-2.5 pt-2.5 sm:space-y-3 sm:px-7 sm:pb-4 sm:pt-4 lg:space-y-0 lg:py-0 min-[1600px]:flex min-[1600px]:min-h-11 min-[1600px]:items-center min-[1600px]:gap-3 min-[1600px]:py-1";
@@ -4,6 +4,9 @@ import { readFileSync } from "node:fs";
const actionsSource = readFileSync(new URL("./MemoEditorHeaderActions.tsx", import.meta.url), "utf8");
const editorSource = readFileSync(new URL("./EditorPane.tsx", import.meta.url), "utf8");
const diagramSource = readFileSync(new URL("./DiagramEditorPane.tsx", import.meta.url), "utf8");
const densitySource = readFileSync(new URL("./MemoEditorChromeDensity.ts", import.meta.url), "utf8");
const editorToolbarSource = readFileSync(new URL("./EditorToolbar.tsx", import.meta.url), "utf8");
const diagramToolbarSource = readFileSync(new URL("./DiagramToolbar.tsx", import.meta.url), "utf8");
describe("shared memo editor header actions", () => {
test("owns every action shared by text and diagram notes", () => {
@@ -24,4 +27,14 @@ describe("shared memo editor header actions", () => {
expect(diagramSource).not.toContain("textNoteActions=");
expect(diagramSource).not.toContain("<WeChatIcon");
});
test("applies one shared compact density standard to both editors", () => {
expect(editorSource).toContain("MEMO_EDITOR_TOP_ROW_CLASS_NAME");
expect(diagramSource).toContain("MEMO_EDITOR_TOP_ROW_CLASS_NAME");
expect(editorToolbarSource).toContain("MEMO_EDITOR_TOOLBAR_PADDING_CLASS_NAME");
expect(diagramToolbarSource).toContain("MEMO_EDITOR_TOOLBAR_PADDING_CLASS_NAME");
expect(densitySource).toContain("sm:min-h-10 sm:px-5 sm:py-1");
expect(densitySource).toContain("min-[1600px]:min-h-11");
expect(densitySource).toContain("min-[1600px]:flex");
});
});