From 7ec73698ab9576f351d9028f6cf2ec1bf983abcb Mon Sep 17 00:00:00 2001
From: SiriusXT <1160925501@qq.com>
Date: Mon, 14 Apr 2025 17:20:35 +0800
Subject: [PATCH 1/6] Allow the tab row scroll
---
src/public/app/widgets/tab_row.ts | 254 ++++++++++++++++----
src/public/stylesheets/theme-next/shell.css | 11 +-
2 files changed, 220 insertions(+), 45 deletions(-)
diff --git a/src/public/app/widgets/tab_row.ts b/src/public/app/widgets/tab_row.ts
index 4de766357..a1c731971 100644
--- a/src/public/app/widgets/tab_row.ts
+++ b/src/public/app/widgets/tab_row.ts
@@ -11,10 +11,11 @@ import type NoteContext from "../components/note_context.js";
const isDesktop = utils.isDesktop();
-const TAB_CONTAINER_MIN_WIDTH = 24;
+const TAB_CONTAINER_MIN_WIDTH = 100;
const TAB_CONTAINER_MAX_WIDTH = 240;
const TAB_CONTAINER_LEFT_PADDING = 5;
-const NEW_TAB_WIDTH = 32;
+const SCROLL_BUTTON_WIDTH = 36;
+const NEW_TAB_WIDTH = 36;
const MIN_FILLER_WIDTH = isDesktop ? 50 : 15;
const MARGIN_WIDTH = 5;
@@ -32,6 +33,8 @@ const TAB_TPL = `
`;
+const CONTAINER_ANCHOR_TPL = `
`;
+
const NEW_TAB_BUTTON_TPL = `+
`;
const FILLER_TPL = ``;
@@ -39,6 +42,7 @@ const TAB_ROW_TPL = `
`;
export default class TabRowWidget extends BasicWidget {
@@ -244,11 +307,24 @@ export default class TabRowWidget extends BasicWidget {
private draggabillyDragging?: Draggabilly | null;
private $style!: JQuery;
+ private $tabScrollingContainer!: JQuery;
+ private $tabContainer!: JQuery;
+ private $scrollButtonLeft!: JQuery;
+ private $scrollButtonRight!: JQuery;
+ private $containerAnchor!: JQuery;
private $filler!: JQuery;
private $newTab!: JQuery;
+ private updateScrollTimeout: ReturnType | undefined;
+
+ private newTabOuterWidth: number = 0;
+ private scrollButtonsOuterWidth: number = 0;
doRender() {
this.$widget = $(TAB_ROW_TPL);
+ this.$tabScrollingContainer = this.$widget.children(".tab-row-widget-scrolling-container");
+ this.$tabContainer = this.$widget.find(".tab-row-widget-container");
+ this.$scrollButtonLeft = this.$widget.children(".tab-scroll-button-left");
+ this.$scrollButtonRight = this.$widget.children(".tab-scroll-button-right");
const documentStyle = window.getComputedStyle(document.documentElement);
this.showNoteIcons = documentStyle.getPropertyValue("--tab-note-icons") === "true";
@@ -257,11 +333,13 @@ export default class TabRowWidget extends BasicWidget {
this.setupStyle();
this.setupEvents();
+ this.setupContainerAnchor();
this.setupDraggabilly();
this.setupNewButton();
this.setupFiller();
this.layoutTabs();
this.setVisibility();
+ this.setupScrollEvents();
this.$widget.on("contextmenu", ".note-tab", (e) => {
e.preventDefault();
@@ -300,6 +378,54 @@ export default class TabRowWidget extends BasicWidget {
this.$widget.append(this.$style);
}
+ scrollTabContainer(direction: number) {
+ const currentScrollLeft = this.$tabScrollingContainer?.scrollLeft() ?? 0;
+ this.$tabScrollingContainer[0].scrollTo({
+ left: currentScrollLeft + direction,
+ behavior: "smooth"
+ });
+ };
+
+ setupScrollEvents() {
+ this.$scrollButtonLeft[0].addEventListener('click', () => this.scrollTabContainer(-200));
+ this.$scrollButtonRight[0].addEventListener('click', () => this.scrollTabContainer(200));
+
+ this.$tabScrollingContainer[0].addEventListener('wheel', (event) => {
+ const targetScrollLeft = event.deltaY*1.5;
+ this.scrollTabContainer(targetScrollLeft);
+ });
+
+ this.$tabScrollingContainer[0].addEventListener('scroll', () => {
+ clearTimeout(this.updateScrollTimeout);
+ this.updateScrollTimeout = setTimeout(() => {
+ this.updateScrollButtonState();
+ }, 100);
+ });
+ }
+
+ updateScrollButtonState() {
+ const scrollLeft = this.$tabScrollingContainer[0].scrollLeft;
+ const scrollWidth = this.$tabScrollingContainer[0].scrollWidth;
+ const clientWidth = this.$tabScrollingContainer[0].clientWidth;
+ // Detect whether the scrollbar is at the far left or far right.
+ this.$scrollButtonLeft.toggleClass("disabled", Math.abs(scrollLeft) <= 1);
+ this.$scrollButtonRight.toggleClass("disabled", Math.abs(scrollLeft + clientWidth - scrollWidth) <= 1);
+ }
+
+ setScrollButtonVisibility(show: boolean = true) {
+ if (show) {
+ this.$scrollButtonLeft.css("display", "flex");
+ this.$scrollButtonRight.css("display", "flex");
+ clearTimeout(this.updateScrollTimeout);
+ this.updateScrollTimeout = setTimeout(() => {
+ this.updateScrollButtonState();
+ }, 200);
+ } else {
+ this.$scrollButtonLeft.css("display", "none");
+ this.$scrollButtonRight.css("display", "none");
+ }
+ }
+
setupEvents() {
new ResizeObserver((_) => {
this.cleanUpPreviouslyDraggedTabs();
@@ -317,14 +443,32 @@ export default class TabRowWidget extends BasicWidget {
return Array.prototype.slice.call(this.$widget.find(".note-tab"));
}
- get $tabContainer() {
- return this.$widget.find(".tab-row-widget-container");
+ updateOuterWidth() {
+ if (this.newTabOuterWidth == 0) {
+ this.newTabOuterWidth = this.$newTab?.outerWidth(true) ?? 0;
+ }
+ if (this.scrollButtonsOuterWidth == 0) {
+ this.scrollButtonsOuterWidth = (this.$scrollButtonLeft?.outerWidth(true) ?? 0) + (this.$scrollButtonRight?.outerWidth(true) ?? 0);
+ }
}
+
get tabWidths() {
const numberOfTabs = this.tabEls.length;
- const tabsContainerWidth = this.$tabContainer[0].clientWidth - NEW_TAB_WIDTH - MIN_FILLER_WIDTH;
- const marginWidth = (numberOfTabs - 1) * MARGIN_WIDTH;
+ // this.$newTab may include margin, and using NEW_TAB_WIDTH could cause tabsContainerWidth to be slightly larger,
+ // resulting in misaligned scrollbars/buttons. Therefore, use outerwidth.
+ this.updateOuterWidth();
+ let tabsContainerWidth = Math.floor(this.$widget.width() ?? 0);
+ tabsContainerWidth -= this.newTabOuterWidth + MIN_FILLER_WIDTH;
+ // Check whether the scroll buttons need to be displayed.
+ if ((TAB_CONTAINER_MIN_WIDTH + MARGIN_WIDTH) * numberOfTabs > tabsContainerWidth) {
+ tabsContainerWidth -= this.scrollButtonsOuterWidth;
+ this.setScrollButtonVisibility(true);
+ } else {
+ this.setScrollButtonVisibility(false);
+ }
+
+ const marginWidth = (numberOfTabs - 1) * MARGIN_WIDTH + TAB_CONTAINER_LEFT_PADDING;
const targetWidth = (tabsContainerWidth - marginWidth) / numberOfTabs;
const clampedTargetWidth = Math.max(TAB_CONTAINER_MIN_WIDTH, Math.min(TAB_CONTAINER_MAX_WIDTH, targetWidth));
const flooredClampedTargetWidth = Math.floor(clampedTargetWidth);
@@ -344,10 +488,6 @@ export default class TabRowWidget extends BasicWidget {
}
}
- if (this.$filler) {
- this.$filler.css("width", `${extraWidthRemaining + MIN_FILLER_WIDTH}px`);
- }
-
return widths;
}
@@ -362,10 +502,9 @@ export default class TabRowWidget extends BasicWidget {
position -= MARGIN_WIDTH; // the last margin should not be applied
- const newTabPosition = position;
- const fillerPosition = position + 32;
+ const anchorPosition = position;
- return { tabPositions, newTabPosition, fillerPosition };
+ return { tabPositions, anchorPosition };
}
layoutTabs() {
@@ -386,15 +525,14 @@ export default class TabRowWidget extends BasicWidget {
let styleHTML = "";
- const { tabPositions, newTabPosition, fillerPosition } = this.getTabPositions();
+ const { tabPositions, anchorPosition } = this.getTabPositions();
tabPositions.forEach((position, i) => {
styleHTML += `.note-tab:nth-child(${i + 1}) { transform: translate3d(${position}px, 0, 0)} `;
});
- styleHTML += `.note-new-tab { transform: translate3d(${newTabPosition}px, 0, 0) } `;
- styleHTML += `.tab-row-filler { transform: translate3d(${fillerPosition}px, 0, 0) } `;
-
+ styleHTML += `.tab-row-container-anchor { transform: translate3d(${anchorPosition}px, 0, 0) } `;
+ styleHTML += `.tab-row-widget-container {width: ${anchorPosition}px}`;
this.$style.html(styleHTML);
}
@@ -406,8 +544,7 @@ export default class TabRowWidget extends BasicWidget {
$tab.addClass("note-tab-was-just-added");
setTimeout(() => $tab.removeClass("note-tab-was-just-added"), 500);
-
- this.$newTab.before($tab);
+ this.$containerAnchor.before($tab);
this.setVisibility();
this.setTabCloseEvent($tab);
this.updateTitle($tab, t("tab_row.new_tab"));
@@ -507,6 +644,7 @@ export default class TabRowWidget extends BasicWidget {
setupDraggabilly() {
const tabEls = this.tabEls;
const { tabPositions } = this.getTabPositions();
+ let initialScrollLeft = 0;
if (this.isDragging && this.draggabillyDragging) {
this.isDragging = false;
@@ -542,11 +680,20 @@ export default class TabRowWidget extends BasicWidget {
this.draggabillyDragging = draggabilly;
tabEl.classList.add("note-tab-is-dragging");
this.$widget.addClass("tab-row-widget-is-sorting");
+
+ initialScrollLeft = this.$tabScrollingContainer?.scrollLeft() ?? 0;
+ draggabilly.positionDrag = () => { };
});
draggabilly.on("dragEnd", () => {
this.isDragging = false;
- const finalTranslateX = parseFloat(tabEl.style.left);
+ const currentScrollLeft = this.$tabScrollingContainer?.scrollLeft() ?? 0;
+ const scrollDelta = currentScrollLeft - initialScrollLeft;
+ const translateX = parseFloat(tabEl.style.left) + scrollDelta;
+ const maxTranslateX = this.$tabContainer[0]?.offsetWidth - tabEl.offsetWidth;
+ const minTranslateX = 0;
+ const finalTranslateX = Math.min(maxTranslateX, Math.max(minTranslateX, translateX));
+
tabEl.style.transform = `translate3d(0, 0, 0)`;
// Animate dragged tab back into its place
@@ -570,12 +717,31 @@ export default class TabRowWidget extends BasicWidget {
});
});
- draggabilly.on("dragMove", (event: unknown, pointer: unknown, moveVector: MoveVector) => {
+ draggabilly.on("dragMove", (event: unknown, pointer: PointerEvent, moveVector: MoveVector) => {
// The current index be computed within the event since it can change during the dragMove
const tabEls = this.tabEls;
const currentIndex = tabEls.indexOf(tabEl);
- const currentTabPositionX = originalTabPositionX + moveVector.x;
+ const scorllContainerBounds = this.$tabScrollingContainer[0]?.getBoundingClientRect();
+ const pointerX = pointer.pageX;
+ const scrollSpeed = 100; // The increment of each scroll.
+ // Check if the mouse is near the edge of the container and trigger scrolling.
+ if (pointerX < scorllContainerBounds.left) {
+ this.scrollTabContainer(- scrollSpeed);
+ } else if (pointerX > scorllContainerBounds.right) {
+ this.scrollTabContainer(scrollSpeed);
+ }
+
+ const currentScrollLeft = this.$tabScrollingContainer?.scrollLeft() ?? 0;
+ const scrollDelta = currentScrollLeft - initialScrollLeft;
+ let translateX = moveVector.x + scrollDelta;
+
+ // Limit the `translateX` so that `tabEl` cannot exceed the left and right boundaries of the container.
+ const maxTranslateX = this.$tabContainer[0]?.offsetWidth - tabEl.offsetWidth - originalTabPositionX;
+ const minTranslateX = - originalTabPositionX;
+ translateX = Math.min(maxTranslateX, Math.max(minTranslateX, translateX));
+ tabEl.style.transform = `translate3d(${translateX}px, 0, 0)`;
+ const currentTabPositionX = originalTabPositionX + translateX;
const destinationIndexTarget = this.closest(currentTabPositionX, tabPositions);
const destinationIndex = Math.max(0, Math.min(tabEls.length, destinationIndexTarget));
@@ -594,8 +760,7 @@ export default class TabRowWidget extends BasicWidget {
if (destinationIndex < originIndex) {
tabEl.parentNode?.insertBefore(tabEl, this.tabEls[destinationIndex]);
} else {
- const beforeEl = this.tabEls[destinationIndex + 1] || this.$newTab[0];
-
+ const beforeEl = this.tabEls[destinationIndex + 1] || this.$containerAnchor[0];
tabEl.parentNode?.insertBefore(tabEl, beforeEl);
}
this.triggerEvent("tabReorder", { ntxIdsInOrder: this.getNtxIdsInOrder() });
@@ -604,14 +769,19 @@ export default class TabRowWidget extends BasicWidget {
setupNewButton() {
this.$newTab = $(NEW_TAB_BUTTON_TPL);
-
- this.$tabContainer.append(this.$newTab);
+ this.$widget.append(this.$newTab);
}
setupFiller() {
this.$filler = $(FILLER_TPL);
- this.$tabContainer.append(this.$filler);
+ this.$widget.append(this.$filler);
+ }
+
+ setupContainerAnchor() {
+ this.$containerAnchor = $(CONTAINER_ANCHOR_TPL);
+
+ this.$tabContainer.append(this.$containerAnchor);
}
closest(value: number, array: number[]) {
@@ -660,7 +830,9 @@ export default class TabRowWidget extends BasicWidget {
updateTabById(ntxId: string | null) {
const $tab = this.getTabById(ntxId);
-
+ $tab[0].scrollIntoView({
+ behavior: 'smooth'
+ });
const noteContext = appContext.tabManager.getNoteContextById(ntxId);
this.updateTab($tab, noteContext);
diff --git a/src/public/stylesheets/theme-next/shell.css b/src/public/stylesheets/theme-next/shell.css
index da8f3bb1d..81ecff173 100644
--- a/src/public/stylesheets/theme-next/shell.css
+++ b/src/public/stylesheets/theme-next/shell.css
@@ -871,7 +871,6 @@ body.mobile .fancytree-node > span {
/* #region Apply a border to the tab bar that avoids the current tab but also allows a transparent active tab. */
body.layout-horizontal .tab-row-widget,
-body.layout-horizontal .tab-row-widget-container,
body.layout-horizontal .tab-row-container .note-tab[active] {
overflow: visible !important;
}
@@ -905,10 +904,13 @@ body.layout-vertical.electron.platform-darwin .tab-row-container {
}
.tab-row-widget-container {
- margin-top: calc((var(--tab-bar-height) - var(--tab-height)) / 2);
height: var(--tab-height) !important;
}
+.tab-row-widget {
+ padding-top: calc((var(--tab-bar-height) - var(--tab-height)) / 2);
+}
+
body.layout-horizontal .tab-row-container {
padding-top: calc((var(--tab-bar-height) - var(--tab-height)));
}
@@ -923,13 +925,14 @@ body.layout-vertical #left-pane .quick-search {
/* Limit the drag area for the previous elements to include just to the element itself
and not its descendants also */
body.layout-horizontal .tab-row-container > *,
-body.layout-vertical .tab-row-widget > *,
+body.layout-vertical .tab-row-widget > *:not(.tab-row-filler),
body.layout-vertical #left-pane .quick-search > * {
-webkit-app-region: no-drag;
}
+body.layout-horizontal .tab-row-widget,
body.layout-horizontal .tab-row-widget-container {
- margin-top: 0;
+ padding-top: 0;
position: relative;
overflow: hidden;
}
From 91231874e3b92eadafe83e0870f4dd14baea99ae Mon Sep 17 00:00:00 2001
From: SiriusXT <1160925501@qq.com>
Date: Mon, 14 Apr 2025 18:43:54 +0800
Subject: [PATCH 2/6] Avoid triggering tab switch on long press.
---
src/public/app/types-lib.d.ts | 2 +-
src/public/app/widgets/tab_row.ts | 18 ++++++++++++------
2 files changed, 13 insertions(+), 7 deletions(-)
diff --git a/src/public/app/types-lib.d.ts b/src/public/app/types-lib.d.ts
index 51155abfb..770021751 100644
--- a/src/public/app/types-lib.d.ts
+++ b/src/public/app/types-lib.d.ts
@@ -13,7 +13,7 @@ declare module "draggabilly" {
containment: HTMLElement
});
element: HTMLElement;
- on(event: "pointerDown" | "dragStart" | "dragEnd" | "dragMove", callback: Callback);
+ on(event: "pointerDown" | "pointerUp" | "dragStart" | "dragEnd" | "dragMove", callback: Callback);
dragEnd();
isDragging: boolean;
positionDrag: () => void;
diff --git a/src/public/app/widgets/tab_row.ts b/src/public/app/widgets/tab_row.ts
index a1c731971..44299aa8e 100644
--- a/src/public/app/widgets/tab_row.ts
+++ b/src/public/app/widgets/tab_row.ts
@@ -388,13 +388,13 @@ export default class TabRowWidget extends BasicWidget {
setupScrollEvents() {
this.$scrollButtonLeft[0].addEventListener('click', () => this.scrollTabContainer(-200));
- this.$scrollButtonRight[0].addEventListener('click', () => this.scrollTabContainer(200));
+ this.$scrollButtonRight[0].addEventListener('click', () => this.scrollTabContainer(200));
this.$tabScrollingContainer[0].addEventListener('wheel', (event) => {
- const targetScrollLeft = event.deltaY*1.5;
+ const targetScrollLeft = event.deltaY * 1.5;
this.scrollTabContainer(targetScrollLeft);
});
-
+
this.$tabScrollingContainer[0].addEventListener('scroll', () => {
clearTimeout(this.updateScrollTimeout);
this.updateScrollTimeout = setTimeout(() => {
@@ -458,7 +458,7 @@ export default class TabRowWidget extends BasicWidget {
// this.$newTab may include margin, and using NEW_TAB_WIDTH could cause tabsContainerWidth to be slightly larger,
// resulting in misaligned scrollbars/buttons. Therefore, use outerwidth.
this.updateOuterWidth();
- let tabsContainerWidth = Math.floor(this.$widget.width() ?? 0);
+ let tabsContainerWidth = Math.floor(this.$widget.width() ?? 0);
tabsContainerWidth -= this.newTabOuterWidth + MIN_FILLER_WIDTH;
// Check whether the scroll buttons need to be displayed.
if ((TAB_CONTAINER_MIN_WIDTH + MARGIN_WIDTH) * numberOfTabs > tabsContainerWidth) {
@@ -671,8 +671,14 @@ export default class TabRowWidget extends BasicWidget {
this.draggabillies.push(draggabilly);
+ let pointerDownTime: number = 0;
draggabilly.on("pointerDown", () => {
- appContext.tabManager.activateNoteContext(tabEl.getAttribute("data-ntx-id"));
+ pointerDownTime = Date.now();
+ });
+ draggabilly.on("pointerUp", () => {
+ if (Date.now() - pointerDownTime < 200) {
+ appContext.tabManager.activateNoteContext(tabEl.getAttribute("data-ntx-id"));
+ }
});
draggabilly.on("dragStart", () => {
@@ -780,7 +786,7 @@ export default class TabRowWidget extends BasicWidget {
setupContainerAnchor() {
this.$containerAnchor = $(CONTAINER_ANCHOR_TPL);
-
+
this.$tabContainer.append(this.$containerAnchor);
}
From 83327b290394baf33ca5c4c92809db8b95941ad8 Mon Sep 17 00:00:00 2001
From: SiriusXT <1160925501@qq.com>
Date: Tue, 15 Apr 2025 19:43:28 +0800
Subject: [PATCH 3/6] Fix horizontal line issue in horizontal view and drag
animation in vertical view.
---
src/public/app/widgets/tab_row.ts | 1 -
src/public/stylesheets/theme-next/shell.css | 37 ++++-----------------
2 files changed, 6 insertions(+), 32 deletions(-)
diff --git a/src/public/app/widgets/tab_row.ts b/src/public/app/widgets/tab_row.ts
index 44299aa8e..3e47acb70 100644
--- a/src/public/app/widgets/tab_row.ts
+++ b/src/public/app/widgets/tab_row.ts
@@ -47,7 +47,6 @@ const TAB_ROW_TPL = `
position: relative;
width: 100%;
background: var(--main-background-color);
- overflow: hidden;
user-select: none;
}
diff --git a/src/public/stylesheets/theme-next/shell.css b/src/public/stylesheets/theme-next/shell.css
index 81ecff173..483d6910d 100644
--- a/src/public/stylesheets/theme-next/shell.css
+++ b/src/public/stylesheets/theme-next/shell.css
@@ -869,36 +869,11 @@ body.mobile .fancytree-node > span {
position: relative;
}
-/* #region Apply a border to the tab bar that avoids the current tab but also allows a transparent active tab. */
-body.layout-horizontal .tab-row-widget,
-body.layout-horizontal .tab-row-container .note-tab[active] {
- overflow: visible !important;
-}
-
-body.layout-horizontal .tab-row-container .note-tab[active]:before {
- content: "";
- position: absolute;
- bottom: 0;
- left: -100vw;
- top: var(--tab-height);
- right: calc(100% - 1px);
- height: 1px;
+/* Apply a border to the tab bar that avoids the current tab but also allows a transparent active tab. */
+body.layout-horizontal .tab-row-container {
border-bottom: 1px solid var(--launcher-pane-horiz-border-color);
}
-body.layout-horizontal .tab-row-container .note-tab[active]:after {
- content: "";
- position: absolute;
- bottom: 0;
- left: 100%;
- top: var(--tab-height);
- right: 0;
- width: 100vw;
- height: 1px;
- border-bottom: 1px solid var(--launcher-pane-horiz-border-color);
-}
-/* #endregion */
-
body.layout-vertical.electron.platform-darwin .tab-row-container {
border-bottom: 1px solid var(--subtle-border-color);
}
@@ -907,8 +882,8 @@ body.layout-vertical.electron.platform-darwin .tab-row-container {
height: var(--tab-height) !important;
}
-.tab-row-widget {
- padding-top: calc((var(--tab-bar-height) - var(--tab-height)) / 2);
+.tab-row-widget > * {
+ margin-top: calc((var(--tab-bar-height) - var(--tab-height)) / 2);
}
body.layout-horizontal .tab-row-container {
@@ -932,7 +907,7 @@ body.layout-vertical #left-pane .quick-search > * {
body.layout-horizontal .tab-row-widget,
body.layout-horizontal .tab-row-widget-container {
- padding-top: 0;
+ margin-top: 0;
position: relative;
overflow: hidden;
}
@@ -994,7 +969,7 @@ body.layout-horizontal .tab-row-widget .note-tab .note-tab-wrapper {
text-overflow: ellipsis;
}
-body.layout-vertical .tab-row-widget-is-sorting .note-tab[active] .note-tab-wrapper {
+body.layout-vertical .tab-row-widget-is-sorting .note-tab.note-tab-is-dragging .note-tab-wrapper {
transform: scale(0.85);
box-shadow: var(--active-tab-dragging-shadow) !important;
}
From 897fde73324d8e31b43d6ef8efe3b87cf03b9754 Mon Sep 17 00:00:00 2001
From: SiriusXT <1160925501@qq.com>
Date: Tue, 15 Apr 2025 22:09:55 +0800
Subject: [PATCH 4/6] Fix lag when scrolling the tab row.
---
src/public/app/widgets/tab_row.ts | 22 ++++++++++++++--------
1 file changed, 14 insertions(+), 8 deletions(-)
diff --git a/src/public/app/widgets/tab_row.ts b/src/public/app/widgets/tab_row.ts
index 3e47acb70..e5c93ab99 100644
--- a/src/public/app/widgets/tab_row.ts
+++ b/src/public/app/widgets/tab_row.ts
@@ -377,23 +377,29 @@ export default class TabRowWidget extends BasicWidget {
this.$widget.append(this.$style);
}
- scrollTabContainer(direction: number) {
- const currentScrollLeft = this.$tabScrollingContainer?.scrollLeft() ?? 0;
+ scrollTabContainer(direction: number, behavior: ScrollBehavior = "smooth") {
+ const currentScrollLeft = this.$tabScrollingContainer[0]?.scrollLeft;
this.$tabScrollingContainer[0].scrollTo({
left: currentScrollLeft + direction,
- behavior: "smooth"
+ behavior
});
};
setupScrollEvents() {
+ let isScrolling = false;
+ this.$tabScrollingContainer[0].addEventListener('wheel', (event) => {
+ if (!isScrolling) {
+ isScrolling = true;
+ requestAnimationFrame(() => {
+ this.scrollTabContainer(event.deltaY * 1.5, 'instant');
+ isScrolling = false;
+ });
+ }
+ });
+
this.$scrollButtonLeft[0].addEventListener('click', () => this.scrollTabContainer(-200));
this.$scrollButtonRight[0].addEventListener('click', () => this.scrollTabContainer(200));
- this.$tabScrollingContainer[0].addEventListener('wheel', (event) => {
- const targetScrollLeft = event.deltaY * 1.5;
- this.scrollTabContainer(targetScrollLeft);
- });
-
this.$tabScrollingContainer[0].addEventListener('scroll', () => {
clearTimeout(this.updateScrollTimeout);
this.updateScrollTimeout = setTimeout(() => {
From d1c2672f99dd2137e1c49cb692349b367c10453d Mon Sep 17 00:00:00 2001
From: SiriusXT <1160925501@qq.com>
Date: Tue, 15 Apr 2025 23:38:08 +0800
Subject: [PATCH 5/6] Fix trackpad not switching tabs.
---
src/public/app/types-lib.d.ts | 2 +-
src/public/app/widgets/tab_row.ts | 10 ++--------
2 files changed, 3 insertions(+), 9 deletions(-)
diff --git a/src/public/app/types-lib.d.ts b/src/public/app/types-lib.d.ts
index 770021751..57b810f76 100644
--- a/src/public/app/types-lib.d.ts
+++ b/src/public/app/types-lib.d.ts
@@ -13,7 +13,7 @@ declare module "draggabilly" {
containment: HTMLElement
});
element: HTMLElement;
- on(event: "pointerDown" | "pointerUp" | "dragStart" | "dragEnd" | "dragMove", callback: Callback);
+ on(event: "staticClick" | "dragStart" | "dragEnd" | "dragMove", callback: Callback);
dragEnd();
isDragging: boolean;
positionDrag: () => void;
diff --git a/src/public/app/widgets/tab_row.ts b/src/public/app/widgets/tab_row.ts
index e5c93ab99..c5fb4e582 100644
--- a/src/public/app/widgets/tab_row.ts
+++ b/src/public/app/widgets/tab_row.ts
@@ -676,14 +676,8 @@ export default class TabRowWidget extends BasicWidget {
this.draggabillies.push(draggabilly);
- let pointerDownTime: number = 0;
- draggabilly.on("pointerDown", () => {
- pointerDownTime = Date.now();
- });
- draggabilly.on("pointerUp", () => {
- if (Date.now() - pointerDownTime < 200) {
- appContext.tabManager.activateNoteContext(tabEl.getAttribute("data-ntx-id"));
- }
+ draggabilly.on("staticClick", () => {
+ appContext.tabManager.activateNoteContext(tabEl.getAttribute("data-ntx-id"));
});
draggabilly.on("dragStart", () => {
From 80f895a2d536280ecf2c55b5307a1d49a1563486 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Tue, 15 Apr 2025 21:26:04 +0300
Subject: [PATCH 6/6] chore(release): mention feature in changelog
---
docs/Release Notes/Release Notes/v0.93.0.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/docs/Release Notes/Release Notes/v0.93.0.md b/docs/Release Notes/Release Notes/v0.93.0.md
index 56726e070..fd13d57b4 100644
--- a/docs/Release Notes/Release Notes/v0.93.0.md
+++ b/docs/Release Notes/Release Notes/v0.93.0.md
@@ -34,6 +34,7 @@
* Native ARM builds for Windows are now back.
* Basic Touch Bar support for macOS.
* [Support Bearer Token](https://github.com/TriliumNext/Notes/issues/1701)
+* The tab bar is now scrollable when there are many tabs by @SiriusXT
## 🌍 Internationalization