Skip to content

Commit

Permalink
fix: ux fix
Browse files Browse the repository at this point in the history
  • Loading branch information
pengx17 committed Aug 29, 2021
1 parent cb6ab8d commit ae63d8f
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 39 deletions.
6 changes: 4 additions & 2 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@

A plugin that let's you to manage your working pages with tabs.

Note: the UX is not stable yet. You may find it change rapidly during different versions.

UX is mainly brought from morden browsers:
- normally, if a new page is visited, the current tab will be replaced by the new page
- if you click a page link while holding CTRL(or CMD on Mac) key, a new tab will be created (not visied yet)
- you can remove the tab by click the remove button in each tab
- you can double-click a tab to pin it. A pined tab will not be replaced or be removed.

## TODO
- [ ] Drag and drop tabs
- [ ] Transitions

![](./demo.gif)
2 changes: 1 addition & 1 deletion src/PageTabs.css
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
}

.logseq-tab-title {
@apply overflow-ellipsis max-w-80 px-1 overflow-hidden whitespace-nowrap inline transition;
@apply overflow-ellipsis max-w-80 px-1 overflow-hidden whitespace-nowrap inline transition-all duration-200 ease-in-out;
}

.logseq-tab[data-active="false"] .logseq-tab-title {
Expand Down
77 changes: 42 additions & 35 deletions src/PageTabs.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import type { BlockEntity } from "@logseq/libs/dist/LSPlugin";
import React from "react";
import { useDeepCompareEffect, useLatest } from "react-use";

import "./PageTabs.css";
import { ITabInfo } from "./types";
import {
getSourcePage,
useAdpatMainUIStyle,
useOpeningPageTabs
useOpeningPageTabs,
} from "./utils";


const CloseSVG = () => (
<svg
height="1em"
Expand All @@ -22,20 +23,19 @@ const CloseSVG = () => (
</svg>
);


function isTabActive(tab: ITabInfo, activePage: ITabInfo | null) {
function isEqual(a?: string, b?: string) {
return a?.toLowerCase() === b?.toLowerCase();
}
return Boolean(
activePage &&
(isEqual(tab.name, activePage?.originalName) ||
isEqual(tab.name, activePage.name))
(isEqual(tab.name, activePage?.originalName) ||
isEqual(tab.name, activePage.name))
);
}

const sortTabs = (tabs: ITabInfo[]) => {
const newTabs = [...tabs]
const newTabs = [...tabs];
newTabs.sort((a, b) => {
if (a.pined && !b.pined) {
return -1;
Expand All @@ -45,9 +45,8 @@ const sortTabs = (tabs: ITabInfo[]) => {
return 0;
}
});
return newTabs
}

return newTabs;
};

function Tabs({
activePage,
Expand Down Expand Up @@ -87,7 +86,7 @@ function Tabs({
};
return (
<div
onMouseDown={onClickTab}
onClick={onClickTab}
onDoubleClick={() => onPinTab(tab)}
key={tab.uuid}
data-active={isActive}
Expand Down Expand Up @@ -139,11 +138,10 @@ function useAddPageTab(cb: (e: ITabInfo) => void) {
}, [cb]);
}


/**
* the active page is the page that is currently being viewed
*/
export function useActivePage() {
export function useActivePage() {
const [page, setPage] = React.useState<null | ITabInfo>(null);
const pageRef = React.useRef(page);
async function setActivePage() {
Expand Down Expand Up @@ -174,7 +172,6 @@ function useAddPageTab(cb: (e: ITabInfo) => void) {
return page;
}


export function PageTabs(): JSX.Element {
const [tabs, setTabs] = useOpeningPageTabs();
const activePage = useActivePage();
Expand All @@ -193,17 +190,24 @@ export function PageTabs(): JSX.Element {
newTabs.splice(idx, 1);
setTabs(newTabs);
if (tab.uuid === activePage?.uuid) {
console.log("closing tab");
logseq.App.pushState("page", {
name: tabs.find((t) => t.uuid !== activePage?.uuid)?.name,
name: newTabs[Math.min(newTabs.length - 1, idx)].originalName,
});
}
};

const onNewTab = React.useCallback(
(t: ITabInfo | null) => {
console.log("add");
setTabs((_tabs) => {
if (t && _tabs.every((_t) => _t.uuid !== t.uuid)) {
return [..._tabs, t];
if (t) {
if (_tabs.every((_t) => _t.uuid !== t.uuid)) {
return [..._tabs, t];
} else {
// If it is already in the tab, just make it active
logseq.App.pushState("page", { name: t.originalName });
}
}
return _tabs;
});
Expand All @@ -213,28 +217,31 @@ export function PageTabs(): JSX.Element {

useAddPageTab(onNewTab);

React.useEffect(() => {
if (activePage) {
setTabs((tabs) => {
if (tabs.every((t) => t.uuid !== activePage?.uuid)) {
let replaceIndex = tabs.findIndex(
(t) => t.uuid === activePage.uuid && !t.pined
);
if (replaceIndex === -1) {
replaceIndex = tabs.findIndex((t) => !t.pined);
}
const prevActivePageRef = React.useRef<ITabInfo | null>();
const latestTabsRef = useLatest(tabs);

if (replaceIndex === -1) {
return [...tabs, activePage];
} else {
const newTabs = [...tabs];
newTabs.splice(replaceIndex, 1, activePage);
return newTabs;
}
useDeepCompareEffect(() => {
let newTabs = latestTabsRef.current;
// If a new ActivePage is set, we will need to replace or insert the tab
if (activePage) {
// if new active page is NOT in the tabs
// - if current active page is pined, insert new tab at the end
// - if there is no
if (tabs.every((t) => t.uuid !== activePage?.uuid)) {
newTabs = [...tabs];
const currentIndex = tabs.findIndex(
(t) => t.uuid === prevActivePageRef.current?.uuid
);
const currentPinned = tabs[currentIndex]?.pined;
if (currentIndex === -1 || currentPinned) {
newTabs.push(activePage);
} else {
newTabs[currentIndex] = activePage;
}
return tabs;
});
}
}
prevActivePageRef.current = activePage;
setTabs(newTabs);
}, [activePage, setTabs]);

const onPinTab = React.useCallback(
Expand Down
2 changes: 1 addition & 1 deletion src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ export function useAdpatMainUIStyle() {
logseq.setMainUIInlineStyle({
zIndex: 9,
top: `${topOffset + 2}px`,
width: Math.min(width) + "px",
width: (width - 10) + "px", // 10 is the width of the scrollbar
transition: "width 0.2s",
});
}
Expand Down

0 comments on commit ae63d8f

Please sign in to comment.