From 66b7340268124113e13a6a878b3c321f91d74baf Mon Sep 17 00:00:00 2001 From: 01zulfi <85733202+01zulfi@users.noreply.github.com> Date: Wed, 18 Dec 2024 10:34:29 +0500 Subject: [PATCH] web: add go to next/previous tab shortcut Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com> --- apps/web/__e2e__/editor.test.ts | 52 +++++++++++++++++++ apps/web/src/components/editor/action-bar.tsx | 21 +++++++- apps/web/src/components/editor/tiptap.tsx | 10 +++- apps/web/src/stores/editor-store.ts | 22 ++++++++ 4 files changed, 102 insertions(+), 3 deletions(-) diff --git a/apps/web/__e2e__/editor.test.ts b/apps/web/__e2e__/editor.test.ts index b5b9e22467..1d8b781584 100644 --- a/apps/web/__e2e__/editor.test.ts +++ b/apps/web/__e2e__/editor.test.ts @@ -270,3 +270,55 @@ test("#1468 count words separated by newlines", async ({ page }) => { expect((await notes.editor.getWordCount()) === 10).toBeTruthy(); }); + +test("control + right arrow should go to next note", async ({ page }) => { + const app = new AppModel(page); + await app.goto(); + const notes = await app.goToNotes(); + const note1 = await notes.createNote({ + title: "Note 1", + content: "Note 1 content" + }); + const note2 = await notes.createNote({ + title: "Note 2", + content: "Note 2 content" + }); + + await note1?.openNote(); + await note2?.openNote(); + await page.keyboard.press("Control+ArrowRight"); + + expect(await notes.editor.getTitle()).toBe("Note 1"); + expect(await notes.editor.getContent("text")).toBe("Note 1 content"); + + await page.keyboard.press("Control+ArrowRight"); + + expect(await notes.editor.getTitle()).toBe("Note 2"); + expect(await notes.editor.getContent("text")).toBe("Note 2 content"); +}); + +test("control + left arrow should go to previous note", async ({ page }) => { + const app = new AppModel(page); + await app.goto(); + const notes = await app.goToNotes(); + const note1 = await notes.createNote({ + title: "Note 1", + content: "Note 1 content" + }); + const note2 = await notes.createNote({ + title: "Note 2", + content: "Note 2 content" + }); + + await note1?.openNote(); + await note2?.openNote(); + await page.keyboard.press("Control+ArrowLeft"); + + expect(await notes.editor.getTitle()).toBe("Note 1"); + expect(await notes.editor.getContent("text")).toBe("Note 1 content"); + + await page.keyboard.press("Control+ArrowLeft"); + + expect(await notes.editor.getTitle()).toBe("Note 2"); + expect(await notes.editor.getContent("text")).toBe("Note 2 content"); +}); diff --git a/apps/web/src/components/editor/action-bar.tsx b/apps/web/src/components/editor/action-bar.tsx index 1d4ec53ac2..23cd034716 100644 --- a/apps/web/src/components/editor/action-bar.tsx +++ b/apps/web/src/components/editor/action-bar.tsx @@ -18,7 +18,7 @@ along with this program. If not, see . */ import { Button, Flex, Text } from "@theme-ui/components"; -import { useState } from "react"; +import { useEffect, useRef, useState } from "react"; import { ArrowLeft, Cross, @@ -439,10 +439,27 @@ function Tab(props: TabProps) { : Note; const { attributes, listeners, setNodeRef, transform, transition, active } = useSortable({ id }); + const activeTabRef = useRef(null); + + useEffect(() => { + if (activeTabRef.current) { + const tab = activeTabRef.current; + tab.scrollIntoView({ + behavior: "smooth", + block: "nearest", + inline: "nearest" + }); + } + }, [isActive]); return ( { + setNodeRef(el); + if (isActive) { + activeTabRef.current = el; + } + }} className="tab" sx={{ borderRadius: "default", diff --git a/apps/web/src/components/editor/tiptap.tsx b/apps/web/src/components/editor/tiptap.tsx index 36cb12abbf..ba8aa0ae46 100644 --- a/apps/web/src/components/editor/tiptap.tsx +++ b/apps/web/src/components/editor/tiptap.tsx @@ -165,7 +165,15 @@ function TipTap(props: TipTapProps) { const tiptapOptions = useMemo>(() => { return { editorProps: { - handleKeyDown(view, event) { + handleKeyDown(_, event) { + if (event.ctrlKey && event.key === "ArrowRight") { + event.preventDefault(); + useEditorStore.getState().openNextSession("right"); + } + if (event.ctrlKey && event.key === "ArrowLeft") { + event.preventDefault(); + useEditorStore.getState().openNextSession("left"); + } if ((event.ctrlKey || event.metaKey) && event.key === "s") { event.preventDefault(); onChange?.( diff --git a/apps/web/src/stores/editor-store.ts b/apps/web/src/stores/editor-store.ts index 5434097eda..1ba83ffaf3 100644 --- a/apps/web/src/stores/editor-store.ts +++ b/apps/web/src/stores/editor-store.ts @@ -710,6 +710,28 @@ class EditorStore extends BaseStore { } }; + openNextSession = (dir: "right" | "left") => { + const { sessions, activeSessionId } = this.get(); + if (sessions.length === 0 || sessions.length === 1) return; + + const index = sessions.findIndex((s) => s.id === activeSessionId); + + if (index === -1) return; + + switch (dir) { + case "right": + if (index === sessions.length - 1) { + return this.openSession(sessions[0].id); + } + return this.openSession(sessions[index + 1].id); + case "left": + if (index === 0) { + return this.openSession(sessions[sessions.length - 1].id); + } + return this.openSession(sessions[index - 1].id); + } + }; + addSession = (session: EditorSession, activate = true) => { let oldSessionId: string | null = null;