diff --git a/apps/web/src/components/editor/action-bar.tsx b/apps/web/src/components/editor/action-bar.tsx index 606cfdc9c..8b91914af 100644 --- a/apps/web/src/components/editor/action-bar.tsx +++ b/apps/web/src/components/editor/action-bar.tsx @@ -29,6 +29,7 @@ import { FocusMode, Fullscreen, Lock, + NewTab, NormalMode, Note, NoteRemove, @@ -107,6 +108,12 @@ export function EditorActionBar() { const isTablet = useTablet(); const tools = [ + { + title: strings.newTab(), + icon: NewTab, + enabled: true, + onClick: () => useEditorStore.getState().addTab() + }, { title: strings.undo(), icon: Undo, @@ -244,7 +251,10 @@ export function EditorActionBar() { mr: hasNativeWindowControls && !isMac() && !isMobile && !isTablet ? `calc(100vw - env(titlebar-area-width))` - : 0 + : 1, + pl: 1, + borderLeft: "1px solid var(--border)", + flexShrink: 0 }} > {tools.map((tool) => ( @@ -255,14 +265,13 @@ export function EditorActionBar() { title={tool.title} key={tool.title} sx={{ - height: "100%", + p: 1, alignItems: "center", bg: "transparent", display: [ "hideOnMobile" in tool && tool.hideOnMobile ? "none" : "flex", tool.hidden ? "none" : "flex" ], - borderRadius: 0, flexShrink: 0, "&:hover svg path": { fill: @@ -273,7 +282,7 @@ export function EditorActionBar() { }} onClick={tool.onClick} > - + ))} @@ -288,173 +297,172 @@ function TabStrip() { const canGoForward = useEditorStore((store) => store.canGoForward); return ( - ({ - backgroundColor: "transparent", - "--ms-track-size": "6px" - })} - thumbStyle={() => ({ height: 3 })} - onWheel={(e) => { - const scrollcontainer = document.querySelector(".tabsScroll"); - if (!scrollcontainer) return; - if (e.deltaY > 0) scrollcontainer.scrollLeft += 100; - else if (e.deltaY < 0) scrollcontainer.scrollLeft -= 100; - }} - > + { - e.stopPropagation(); - useEditorStore.getState().addTab(); + onDoubleClick={(e) => e.stopPropagation()} + > + + + + ({ + backgroundColor: "transparent", + "--ms-track-size": "6px" + })} + thumbStyle={() => ({ height: 3 })} + onWheel={(e) => { + const scrollcontainer = document.querySelector(".tabsScroll"); + if (!scrollcontainer) return; + if (e.deltaY > 0) scrollcontainer.scrollLeft += 100; + else if (e.deltaY < 0) scrollcontainer.scrollLeft -= 100; }} - data-test-id="tabs" > e.stopPropagation()} + onDoubleClick={async (e) => { + e.stopPropagation(); + useEditorStore.getState().addTab(); + }} + data-test-id="tabs" > - - - - { - if (from === to) return; - const tabs = useEditorStore.getState().tabs.slice(); - const isToPinned = tabs[to].pinned; - const [fromTab] = tabs.splice(from, 1); + { + if (from === to) return; + const tabs = useEditorStore.getState().tabs.slice(); + const isToPinned = tabs[to].pinned; + const [fromTab] = tabs.splice(from, 1); - // if the tab where this tab is being dropped is pinned, - // let's pin our tab too. - if (isToPinned) { - fromTab.pinned = true; - } - // unpin the tab if it is moved. - else if (fromTab.pinned) fromTab.pinned = false; + // if the tab where this tab is being dropped is pinned, + // let's pin our tab too. + if (isToPinned) { + fromTab.pinned = true; + } + // unpin the tab if it is moved. + else if (fromTab.pinned) fromTab.pinned = false; - tabs.splice(to, 0, fromTab); - useEditorStore.setState({ tabs }); - }} - renderItem={({ item: tab, index: i }) => { - const session = useEditorStore.getState().getSession(tab.sessionId); - if (!session) return null; + tabs.splice(to, 0, fromTab); + useEditorStore.setState({ tabs }); + }} + renderItem={({ item: tab, index: i }) => { + const session = useEditorStore + .getState() + .getSession(tab.sessionId); + if (!session) return null; - const isUnsaved = - session.type === "default" && - session.saveState === SaveState.NotSaved; + const isUnsaved = + session.type === "default" && + session.saveState === SaveState.NotSaved; - return ( - { - if (tab.id !== currentTab) { - useEditorStore.getState().focusTab(tab.id); + return ( + useEditorStore.getState().closeTabs(tab.id)} - onCloseAll={() => - useEditorStore - .getState() - .closeTabs( - ...tabs.filter((s) => !s.pinned).map((s) => s.id) - ) - } - onCloseOthers={() => - useEditorStore - .getState() - .closeTabs( - ...tabs - .filter((s) => s.id !== tab.id && !s.pinned) - .map((s) => s.id) - ) - } - onCloseToTheRight={() => - useEditorStore - .getState() - .closeTabs( - ...tabs - .filter((s, index) => index > i && !s.pinned) - .map((s) => s.id) - ) - } - onCloseToTheLeft={() => - useEditorStore - .getState() - .closeTabs( - ...tabs - .filter((s, index) => index < i && !s.pinned) - .map((s) => s.id) - ) - } - onRevealInList={ - "note" in session - ? () => - AppEventManager.publish( - AppEvents.revealItemInList, - session.note.id, - true - ) - : undefined - } - onPin={() => { - useEditorStore.setState((state) => { - // preview tabs can never be pinned. - state.tabs[i].pinned = !tab.pinned; - state.tabs.sort((a, b) => - a.pinned === b.pinned ? 0 : a.pinned ? -1 : 1 - ); - }); - }} - /> - ); - }} - /> - - - + isUnsaved={isUnsaved} + isActive={tab.id === currentTab} + isPinned={!!tab.pinned} + isLocked={isLockedSession(session)} + type={session.type} + onFocus={() => { + if (tab.id !== currentTab) { + useEditorStore.getState().focusTab(tab.id); + } + }} + onClose={() => useEditorStore.getState().closeTabs(tab.id)} + onCloseAll={() => + useEditorStore + .getState() + .closeTabs( + ...tabs.filter((s) => !s.pinned).map((s) => s.id) + ) + } + onCloseOthers={() => + useEditorStore + .getState() + .closeTabs( + ...tabs + .filter((s) => s.id !== tab.id && !s.pinned) + .map((s) => s.id) + ) + } + onCloseToTheRight={() => + useEditorStore + .getState() + .closeTabs( + ...tabs + .filter((s, index) => index > i && !s.pinned) + .map((s) => s.id) + ) + } + onCloseToTheLeft={() => + useEditorStore + .getState() + .closeTabs( + ...tabs + .filter((s, index) => index < i && !s.pinned) + .map((s) => s.id) + ) + } + onRevealInList={ + "note" in session + ? () => + AppEventManager.publish( + AppEvents.revealItemInList, + session.note.id, + true + ) + : undefined + } + onPin={() => { + useEditorStore.setState((state) => { + // preview tabs can never be pinned. + state.tabs[i].pinned = !tab.pinned; + state.tabs.sort((a, b) => + a.pinned === b.pinned ? 0 : a.pinned ? -1 : 1 + ); + }); + }} + /> + ); + }} + /> + + + ); } @@ -530,8 +538,9 @@ function Tab(props: TabProps) { sx={{ height: "100%", cursor: "pointer", - px: 2, + pl: 2, borderRight: "1px solid var(--border)", + ":last-of-type": { borderRight: 0 }, transform: CSS.Transform.toString(transform), transition, @@ -543,7 +552,7 @@ function Tab(props: TabProps) { flexShrink: 0, ":hover": { "& .closeTabButton": { - visibility: "visible" + opacity: 1 }, bg: isActive ? "hover-selected" : "hover" } @@ -607,14 +616,13 @@ function Tab(props: TabProps) { {...attributes} > { if (e.button == 0) onFocus(); }} > { @@ -653,10 +662,14 @@ function Tab(props: TabProps) { ) : ( { if (e.button == 0) { @@ -665,7 +678,7 @@ function Tab(props: TabProps) { }} className="closeTabButton" data-test-id={"tab-close-button"} - size={16} + size={14} /> )} diff --git a/apps/web/src/components/icons/index.tsx b/apps/web/src/components/icons/index.tsx index 20b486974..2ce356863 100644 --- a/apps/web/src/components/icons/index.tsx +++ b/apps/web/src/components/icons/index.tsx @@ -219,7 +219,8 @@ import { mdiOpenInNew, mdiTagOutline, mdiChatQuestionOutline, - mdiNoteRemoveOutline + mdiNoteRemoveOutline, + mdiTabPlus } from "@mdi/js"; import { useTheme } from "@emotion/react"; import { Theme } from "@notesnook/theme"; @@ -273,7 +274,8 @@ const MDIIconWrapper = memo( (prev, next) => prev.rotate === next.rotate && prev.color === next.color && - prev.title === next.title + prev.title === next.title && + prev.size === next.size ); export type IconProps = FlexProps & Omit; @@ -560,3 +562,4 @@ export const ClearCache = createIcon(mdiBroom); export const OpenInNew = createIcon(mdiOpenInNew); export const Coupon = createIcon(mdiTagOutline); export const Support = createIcon(mdiChatQuestionOutline); +export const NewTab = createIcon(mdiTabPlus);