fix(web): cycle rich text and Markdown from one toolbar button
The two view icons were the same choice. One button now switches between them, and the tooltip names the mode the click will open.
This commit is contained in:
1 parent
b9535e071d
commit
d2ddb07a0f
5 files changed
+36
-47
No files matched your search
@@ -61,12 +61,14 @@ const EditorToolbarButton = ({
|
||||
children,
|
||||
disabled = false,
|
||||
onClick,
|
||||
shortcutLabel,
|
||||
title,
|
||||
}: {
|
||||
active?: boolean;
|
||||
children: ReactNode;
|
||||
disabled?: boolean;
|
||||
onClick: () => void;
|
||||
shortcutLabel?: string | null;
|
||||
title: string;
|
||||
}) => (
|
||||
<Tooltip>
|
||||
@@ -88,7 +90,16 @@ const EditorToolbarButton = ({
|
||||
{children}
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom">{title}</TooltipContent>
|
||||
<TooltipContent side="bottom" className={shortcutLabel ? "flex items-center gap-2" : undefined}>
|
||||
{shortcutLabel ? (
|
||||
<>
|
||||
<span>{title}</span>
|
||||
<kbd className="rounded border border-current/25 bg-current/10 px-1.5 py-0.5 font-mono text-xs leading-none">
|
||||
{shortcutLabel}
|
||||
</kbd>
|
||||
</>
|
||||
) : title}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
@@ -221,6 +232,8 @@ export const EditorToolbar = ({
|
||||
: "plaintext";
|
||||
const activeEditorView = onEditorViewChange ? editorView : markdownMode ? "markdown" : "rich";
|
||||
const showFormattingTools = activeEditorView === "rich";
|
||||
const nextEditorViewMode = EDITOR_VIEW_MODES[activeEditorView === "rich" ? 1 : 0];
|
||||
const canCycleEditorView = Boolean(onEditorViewChange || onMarkdownModeChange);
|
||||
|
||||
useEffect(() => {
|
||||
const controls = controlsRef.current;
|
||||
@@ -328,50 +341,22 @@ export const EditorToolbar = ({
|
||||
!expanded && MEMO_EDITOR_TOOLBAR_COLLAPSED_CLASS_NAME
|
||||
)}
|
||||
>
|
||||
{onEditorViewChange ? (
|
||||
{canCycleEditorView ? (
|
||||
<>
|
||||
<div className="flex shrink-0 items-center" role="group" aria-label={t("editorToolbar.viewMode")}>
|
||||
{EDITOR_VIEW_MODES.map((mode) => (
|
||||
<EditorToolbarButton
|
||||
key={mode.value}
|
||||
title={mode.value === "markdown" && markdownModeShortcutLabel
|
||||
? `${t(mode.labelKey)} (${markdownModeShortcutLabel})`
|
||||
: t(mode.labelKey)}
|
||||
active={activeEditorView === mode.value}
|
||||
disabled={viewSwitchDisabled}
|
||||
onClick={() => onEditorViewChange(mode.value)}
|
||||
>
|
||||
<mode.icon className="h-4 w-4" />
|
||||
</EditorToolbarButton>
|
||||
))}
|
||||
</div>
|
||||
<MemoEditorToolbarDivider className="hidden sm:block" />
|
||||
</>
|
||||
) : onMarkdownModeChange ? (
|
||||
<>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<button
|
||||
className="flex h-8 w-8 shrink-0 items-center justify-center rounded-md text-slate-500 transition hover:bg-slate-100 hover:text-slate-800 disabled:pointer-events-none disabled:opacity-40"
|
||||
type="button"
|
||||
aria-label={markdownMode ? t("editorToolbar.richText") : t("editorToolbar.markdown")}
|
||||
aria-pressed={markdownMode}
|
||||
disabled={readOnly}
|
||||
onMouseDown={(event) => event.preventDefault()}
|
||||
onClick={onMarkdownModeChange}
|
||||
>
|
||||
{markdownMode ? <FileText className="h-4 w-4" /> : <FileCode2 className="h-4 w-4" />}
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom" className="flex items-center gap-2">
|
||||
<span>{markdownMode ? t("editorToolbar.richText") : t("editorToolbar.markdown")}</span>
|
||||
{markdownModeShortcutLabel && (
|
||||
<kbd className="rounded border border-current/25 bg-current/10 px-1.5 py-0.5 font-mono text-xs leading-none">
|
||||
{markdownModeShortcutLabel}
|
||||
</kbd>
|
||||
)}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
<EditorToolbarButton
|
||||
title={t(nextEditorViewMode.labelKey)}
|
||||
shortcutLabel={markdownModeShortcutLabel}
|
||||
disabled={onEditorViewChange ? viewSwitchDisabled : readOnly}
|
||||
onClick={() => {
|
||||
if (onEditorViewChange) {
|
||||
onEditorViewChange(nextEditorViewMode.value);
|
||||
return;
|
||||
}
|
||||
onMarkdownModeChange?.();
|
||||
}}
|
||||
>
|
||||
<nextEditorViewMode.icon className="h-4 w-4" />
|
||||
</EditorToolbarButton>
|
||||
<MemoEditorToolbarDivider className="hidden sm:block" />
|
||||
</>
|
||||
) : null}
|
||||
|
||||
@@ -76,6 +76,13 @@ describe("shared memo editor header actions", () => {
|
||||
expect(densitySource).not.toContain("min-[1600px]:flex");
|
||||
});
|
||||
|
||||
test("cycles rich text and markdown from one toolbar button", () => {
|
||||
expect(editorToolbarSource).toContain("const nextEditorViewMode = EDITOR_VIEW_MODES[activeEditorView === \"rich\" ? 1 : 0]");
|
||||
expect(editorToolbarSource).toContain("onEditorViewChange(nextEditorViewMode.value)");
|
||||
expect(editorToolbarSource).not.toContain("EDITOR_VIEW_MODES.map");
|
||||
expect(editorToolbarSource).not.toContain('t("editorToolbar.viewMode")');
|
||||
});
|
||||
|
||||
test("aligns the note title, notebook row, and article on one reading gutter", () => {
|
||||
expect(editorSource).toContain("MEMO_EDITOR_READING_GUTTER_CLASS_NAME");
|
||||
expect(densitySource).toContain('px-[var(--editor-reading-gutter)]');
|
||||
|
||||
@@ -2259,7 +2259,6 @@ export const enUS = {
|
||||
deleteTable: "Delete table",
|
||||
markdown: "Switch to Markdown source",
|
||||
richText: "Switch to rich text",
|
||||
viewMode: "Note view",
|
||||
appearance: "Appearance and style",
|
||||
blockStyle: "Paragraph style",
|
||||
markdownSource: "Markdown source",
|
||||
|
||||
@@ -2259,7 +2259,6 @@ export const ja = {
|
||||
deleteTable: "表を削除",
|
||||
markdown: "Markdown ソースに切り替え",
|
||||
richText: "リッチテキストに切り替え",
|
||||
viewMode: "ノートの表示",
|
||||
appearance: "外観とスタイル",
|
||||
blockStyle: "段落スタイル",
|
||||
markdownSource: "Markdown ソース",
|
||||
|
||||
@@ -2257,7 +2257,6 @@ export const zhCN = {
|
||||
deleteTable: "删除表格",
|
||||
markdown: "切换到 Markdown 源码",
|
||||
richText: "切换到富文本编辑",
|
||||
viewMode: "笔记视图",
|
||||
appearance: "外观与样式",
|
||||
blockStyle: "段落样式",
|
||||
markdownSource: "Markdown 源码",
|
||||
|
||||
Reference in new issue
Block a user