mirror of
https://github.com/TriliumNext/Notes.git
synced 2025-07-31 04:02:26 +08:00
Allow the tab row scroll
This commit is contained in:
parent
74b3ab313e
commit
7ec73698ab
@ -11,10 +11,11 @@ import type NoteContext from "../components/note_context.js";
|
|||||||
|
|
||||||
const isDesktop = utils.isDesktop();
|
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_MAX_WIDTH = 240;
|
||||||
const TAB_CONTAINER_LEFT_PADDING = 5;
|
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 MIN_FILLER_WIDTH = isDesktop ? 50 : 15;
|
||||||
const MARGIN_WIDTH = 5;
|
const MARGIN_WIDTH = 5;
|
||||||
|
|
||||||
@ -32,6 +33,8 @@ const TAB_TPL = `
|
|||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
|
const CONTAINER_ANCHOR_TPL = `<div class="tab-row-container-anchor"></div>`;
|
||||||
|
|
||||||
const NEW_TAB_BUTTON_TPL = `<div class="note-new-tab" data-trigger-command="openNewTab" title="${t("tab_row.add_new_tab")}">+</div>`;
|
const NEW_TAB_BUTTON_TPL = `<div class="note-new-tab" data-trigger-command="openNewTab" title="${t("tab_row.add_new_tab")}">+</div>`;
|
||||||
const FILLER_TPL = `<div class="tab-row-filler"></div>`;
|
const FILLER_TPL = `<div class="tab-row-filler"></div>`;
|
||||||
|
|
||||||
@ -39,6 +42,7 @@ const TAB_ROW_TPL = `
|
|||||||
<div class="tab-row-widget">
|
<div class="tab-row-widget">
|
||||||
<style>
|
<style>
|
||||||
.tab-row-widget {
|
.tab-row-widget {
|
||||||
|
display:flex;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -59,7 +63,6 @@ const TAB_ROW_TPL = `
|
|||||||
.tab-row-widget .tab-row-widget-container {
|
.tab-row-widget .tab-row-widget-container {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,15 +77,12 @@ const TAB_ROW_TPL = `
|
|||||||
}
|
}
|
||||||
|
|
||||||
.note-new-tab {
|
.note-new-tab {
|
||||||
position: absolute;
|
display: flex;
|
||||||
left: 0;
|
align-items: center;
|
||||||
width: 36px;
|
justify-content: center;
|
||||||
height: 36px;
|
flex: 0 0 ${NEW_TAB_WIDTH}px;
|
||||||
|
height: ${NEW_TAB_WIDTH}px;
|
||||||
padding: 1px;
|
padding: 1px;
|
||||||
border: 0;
|
|
||||||
margin: 0;
|
|
||||||
z-index: 1;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@ -96,11 +96,22 @@ const TAB_ROW_TPL = `
|
|||||||
.tab-row-filler {
|
.tab-row-filler {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
-webkit-app-region: drag;
|
-webkit-app-region: drag;
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
min-width: ${MIN_FILLER_WIDTH}px;
|
||||||
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tab-row-container-anchor{
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
width: 0px;
|
||||||
|
height: 36px;
|
||||||
|
border: 0;
|
||||||
|
margin: 0;
|
||||||
|
z-index: 1;
|
||||||
|
cursor: pointer;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
body.mobile .tab-row-filler {
|
body.mobile .tab-row-filler {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@ -184,6 +195,38 @@ const TAB_ROW_TPL = `
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tab-scroll-button-left, .tab-scroll-button-right {
|
||||||
|
display: none;
|
||||||
|
flex: 0 0 ${SCROLL_BUTTON_WIDTH}px;
|
||||||
|
height: ${SCROLL_BUTTON_WIDTH}px;
|
||||||
|
padding: 1px 1px 1px 1px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-scroll-button-left {
|
||||||
|
color: var(--active-tab-text-color);
|
||||||
|
box-shadow: inset -1px 0 0 0 var(--main-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-scroll-button-right {
|
||||||
|
color: var(--active-tab-text-color);
|
||||||
|
box-shadow: inset 1px 0 0 0 var(--main-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-scroll-button-left.disabled,
|
||||||
|
.tab-scroll-button-right.disabled {
|
||||||
|
color: var(--inactive-tab-text-color);
|
||||||
|
box-shadow: none;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-scroll-button-left:hover,
|
||||||
|
.tab-scroll-button-right:hover {
|
||||||
|
background-color: var(--tab-background-color, var(--inactive-tab-hover-background-color));
|
||||||
|
}
|
||||||
|
|
||||||
.tab-row-widget .note-tab:hover .note-tab-wrapper {
|
.tab-row-widget .note-tab:hover .note-tab-wrapper {
|
||||||
background-color: var(--tab-background-color, var(--inactive-tab-hover-background-color));
|
background-color: var(--tab-background-color, var(--inactive-tab-hover-background-color));
|
||||||
}
|
}
|
||||||
@ -231,9 +274,29 @@ const TAB_ROW_TPL = `
|
|||||||
.tab-row-widget:not(.tab-row-widget-is-sorting) .note-tab.note-tab-was-just-dragged {
|
.tab-row-widget:not(.tab-row-widget-is-sorting) .note-tab.note-tab-was-just-dragged {
|
||||||
transition: transform 120ms ease-in-out;
|
transition: transform 120ms ease-in-out;
|
||||||
}
|
}
|
||||||
|
.tab-row-widget-wrapper {
|
||||||
|
display: flex;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-row-widget-scrolling-container {
|
||||||
|
overflow-x: auto;
|
||||||
|
overflow-y: hidden;
|
||||||
|
scrollbar-width: none; /* Firefox */
|
||||||
|
}
|
||||||
|
/* Chrome/Safari */
|
||||||
|
.tab-row-widget-scrolling-container::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
<div class="tab-scroll-button-left bx bx-chevron-left"></div>
|
||||||
<div class="tab-row-widget-container"></div>
|
<div class="tab-row-widget-scrolling-container">
|
||||||
|
<div class="tab-row-widget-container"></div>
|
||||||
|
</div>
|
||||||
|
<div class="tab-scroll-button-right bx bx-chevron-right"></div>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
export default class TabRowWidget extends BasicWidget {
|
export default class TabRowWidget extends BasicWidget {
|
||||||
@ -244,11 +307,24 @@ export default class TabRowWidget extends BasicWidget {
|
|||||||
private draggabillyDragging?: Draggabilly | null;
|
private draggabillyDragging?: Draggabilly | null;
|
||||||
|
|
||||||
private $style!: JQuery<HTMLElement>;
|
private $style!: JQuery<HTMLElement>;
|
||||||
|
private $tabScrollingContainer!: JQuery<HTMLElement>;
|
||||||
|
private $tabContainer!: JQuery<HTMLElement>;
|
||||||
|
private $scrollButtonLeft!: JQuery<HTMLElement>;
|
||||||
|
private $scrollButtonRight!: JQuery<HTMLElement>;
|
||||||
|
private $containerAnchor!: JQuery<HTMLElement>;
|
||||||
private $filler!: JQuery<HTMLElement>;
|
private $filler!: JQuery<HTMLElement>;
|
||||||
private $newTab!: JQuery<HTMLElement>;
|
private $newTab!: JQuery<HTMLElement>;
|
||||||
|
private updateScrollTimeout: ReturnType<typeof setTimeout> | undefined;
|
||||||
|
|
||||||
|
private newTabOuterWidth: number = 0;
|
||||||
|
private scrollButtonsOuterWidth: number = 0;
|
||||||
|
|
||||||
doRender() {
|
doRender() {
|
||||||
this.$widget = $(TAB_ROW_TPL);
|
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);
|
const documentStyle = window.getComputedStyle(document.documentElement);
|
||||||
this.showNoteIcons = documentStyle.getPropertyValue("--tab-note-icons") === "true";
|
this.showNoteIcons = documentStyle.getPropertyValue("--tab-note-icons") === "true";
|
||||||
@ -257,11 +333,13 @@ export default class TabRowWidget extends BasicWidget {
|
|||||||
|
|
||||||
this.setupStyle();
|
this.setupStyle();
|
||||||
this.setupEvents();
|
this.setupEvents();
|
||||||
|
this.setupContainerAnchor();
|
||||||
this.setupDraggabilly();
|
this.setupDraggabilly();
|
||||||
this.setupNewButton();
|
this.setupNewButton();
|
||||||
this.setupFiller();
|
this.setupFiller();
|
||||||
this.layoutTabs();
|
this.layoutTabs();
|
||||||
this.setVisibility();
|
this.setVisibility();
|
||||||
|
this.setupScrollEvents();
|
||||||
|
|
||||||
this.$widget.on("contextmenu", ".note-tab", (e) => {
|
this.$widget.on("contextmenu", ".note-tab", (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -300,6 +378,54 @@ export default class TabRowWidget extends BasicWidget {
|
|||||||
this.$widget.append(this.$style);
|
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() {
|
setupEvents() {
|
||||||
new ResizeObserver((_) => {
|
new ResizeObserver((_) => {
|
||||||
this.cleanUpPreviouslyDraggedTabs();
|
this.cleanUpPreviouslyDraggedTabs();
|
||||||
@ -317,14 +443,32 @@ export default class TabRowWidget extends BasicWidget {
|
|||||||
return Array.prototype.slice.call(this.$widget.find(".note-tab"));
|
return Array.prototype.slice.call(this.$widget.find(".note-tab"));
|
||||||
}
|
}
|
||||||
|
|
||||||
get $tabContainer() {
|
updateOuterWidth() {
|
||||||
return this.$widget.find(".tab-row-widget-container");
|
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() {
|
get tabWidths() {
|
||||||
const numberOfTabs = this.tabEls.length;
|
const numberOfTabs = this.tabEls.length;
|
||||||
const tabsContainerWidth = this.$tabContainer[0].clientWidth - NEW_TAB_WIDTH - MIN_FILLER_WIDTH;
|
// this.$newTab may include margin, and using NEW_TAB_WIDTH could cause tabsContainerWidth to be slightly larger,
|
||||||
const marginWidth = (numberOfTabs - 1) * MARGIN_WIDTH;
|
// 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 targetWidth = (tabsContainerWidth - marginWidth) / numberOfTabs;
|
||||||
const clampedTargetWidth = Math.max(TAB_CONTAINER_MIN_WIDTH, Math.min(TAB_CONTAINER_MAX_WIDTH, targetWidth));
|
const clampedTargetWidth = Math.max(TAB_CONTAINER_MIN_WIDTH, Math.min(TAB_CONTAINER_MAX_WIDTH, targetWidth));
|
||||||
const flooredClampedTargetWidth = Math.floor(clampedTargetWidth);
|
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;
|
return widths;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -362,10 +502,9 @@ export default class TabRowWidget extends BasicWidget {
|
|||||||
|
|
||||||
position -= MARGIN_WIDTH; // the last margin should not be applied
|
position -= MARGIN_WIDTH; // the last margin should not be applied
|
||||||
|
|
||||||
const newTabPosition = position;
|
const anchorPosition = position;
|
||||||
const fillerPosition = position + 32;
|
|
||||||
|
|
||||||
return { tabPositions, newTabPosition, fillerPosition };
|
return { tabPositions, anchorPosition };
|
||||||
}
|
}
|
||||||
|
|
||||||
layoutTabs() {
|
layoutTabs() {
|
||||||
@ -386,15 +525,14 @@ export default class TabRowWidget extends BasicWidget {
|
|||||||
|
|
||||||
let styleHTML = "";
|
let styleHTML = "";
|
||||||
|
|
||||||
const { tabPositions, newTabPosition, fillerPosition } = this.getTabPositions();
|
const { tabPositions, anchorPosition } = this.getTabPositions();
|
||||||
|
|
||||||
tabPositions.forEach((position, i) => {
|
tabPositions.forEach((position, i) => {
|
||||||
styleHTML += `.note-tab:nth-child(${i + 1}) { transform: translate3d(${position}px, 0, 0)} `;
|
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-container-anchor { transform: translate3d(${anchorPosition}px, 0, 0) } `;
|
||||||
styleHTML += `.tab-row-filler { transform: translate3d(${fillerPosition}px, 0, 0) } `;
|
styleHTML += `.tab-row-widget-container {width: ${anchorPosition}px}`;
|
||||||
|
|
||||||
this.$style.html(styleHTML);
|
this.$style.html(styleHTML);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -406,8 +544,7 @@ export default class TabRowWidget extends BasicWidget {
|
|||||||
$tab.addClass("note-tab-was-just-added");
|
$tab.addClass("note-tab-was-just-added");
|
||||||
|
|
||||||
setTimeout(() => $tab.removeClass("note-tab-was-just-added"), 500);
|
setTimeout(() => $tab.removeClass("note-tab-was-just-added"), 500);
|
||||||
|
this.$containerAnchor.before($tab);
|
||||||
this.$newTab.before($tab);
|
|
||||||
this.setVisibility();
|
this.setVisibility();
|
||||||
this.setTabCloseEvent($tab);
|
this.setTabCloseEvent($tab);
|
||||||
this.updateTitle($tab, t("tab_row.new_tab"));
|
this.updateTitle($tab, t("tab_row.new_tab"));
|
||||||
@ -507,6 +644,7 @@ export default class TabRowWidget extends BasicWidget {
|
|||||||
setupDraggabilly() {
|
setupDraggabilly() {
|
||||||
const tabEls = this.tabEls;
|
const tabEls = this.tabEls;
|
||||||
const { tabPositions } = this.getTabPositions();
|
const { tabPositions } = this.getTabPositions();
|
||||||
|
let initialScrollLeft = 0;
|
||||||
|
|
||||||
if (this.isDragging && this.draggabillyDragging) {
|
if (this.isDragging && this.draggabillyDragging) {
|
||||||
this.isDragging = false;
|
this.isDragging = false;
|
||||||
@ -542,11 +680,20 @@ export default class TabRowWidget extends BasicWidget {
|
|||||||
this.draggabillyDragging = draggabilly;
|
this.draggabillyDragging = draggabilly;
|
||||||
tabEl.classList.add("note-tab-is-dragging");
|
tabEl.classList.add("note-tab-is-dragging");
|
||||||
this.$widget.addClass("tab-row-widget-is-sorting");
|
this.$widget.addClass("tab-row-widget-is-sorting");
|
||||||
|
|
||||||
|
initialScrollLeft = this.$tabScrollingContainer?.scrollLeft() ?? 0;
|
||||||
|
draggabilly.positionDrag = () => { };
|
||||||
});
|
});
|
||||||
|
|
||||||
draggabilly.on("dragEnd", () => {
|
draggabilly.on("dragEnd", () => {
|
||||||
this.isDragging = false;
|
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)`;
|
tabEl.style.transform = `translate3d(0, 0, 0)`;
|
||||||
|
|
||||||
// Animate dragged tab back into its place
|
// 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
|
// The current index be computed within the event since it can change during the dragMove
|
||||||
const tabEls = this.tabEls;
|
const tabEls = this.tabEls;
|
||||||
const currentIndex = tabEls.indexOf(tabEl);
|
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 destinationIndexTarget = this.closest(currentTabPositionX, tabPositions);
|
||||||
const destinationIndex = Math.max(0, Math.min(tabEls.length, destinationIndexTarget));
|
const destinationIndex = Math.max(0, Math.min(tabEls.length, destinationIndexTarget));
|
||||||
|
|
||||||
@ -594,8 +760,7 @@ export default class TabRowWidget extends BasicWidget {
|
|||||||
if (destinationIndex < originIndex) {
|
if (destinationIndex < originIndex) {
|
||||||
tabEl.parentNode?.insertBefore(tabEl, this.tabEls[destinationIndex]);
|
tabEl.parentNode?.insertBefore(tabEl, this.tabEls[destinationIndex]);
|
||||||
} else {
|
} else {
|
||||||
const beforeEl = this.tabEls[destinationIndex + 1] || this.$newTab[0];
|
const beforeEl = this.tabEls[destinationIndex + 1] || this.$containerAnchor[0];
|
||||||
|
|
||||||
tabEl.parentNode?.insertBefore(tabEl, beforeEl);
|
tabEl.parentNode?.insertBefore(tabEl, beforeEl);
|
||||||
}
|
}
|
||||||
this.triggerEvent("tabReorder", { ntxIdsInOrder: this.getNtxIdsInOrder() });
|
this.triggerEvent("tabReorder", { ntxIdsInOrder: this.getNtxIdsInOrder() });
|
||||||
@ -604,14 +769,19 @@ export default class TabRowWidget extends BasicWidget {
|
|||||||
|
|
||||||
setupNewButton() {
|
setupNewButton() {
|
||||||
this.$newTab = $(NEW_TAB_BUTTON_TPL);
|
this.$newTab = $(NEW_TAB_BUTTON_TPL);
|
||||||
|
this.$widget.append(this.$newTab);
|
||||||
this.$tabContainer.append(this.$newTab);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setupFiller() {
|
setupFiller() {
|
||||||
this.$filler = $(FILLER_TPL);
|
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[]) {
|
closest(value: number, array: number[]) {
|
||||||
@ -660,7 +830,9 @@ export default class TabRowWidget extends BasicWidget {
|
|||||||
|
|
||||||
updateTabById(ntxId: string | null) {
|
updateTabById(ntxId: string | null) {
|
||||||
const $tab = this.getTabById(ntxId);
|
const $tab = this.getTabById(ntxId);
|
||||||
|
$tab[0].scrollIntoView({
|
||||||
|
behavior: 'smooth'
|
||||||
|
});
|
||||||
const noteContext = appContext.tabManager.getNoteContextById(ntxId);
|
const noteContext = appContext.tabManager.getNoteContextById(ntxId);
|
||||||
|
|
||||||
this.updateTab($tab, noteContext);
|
this.updateTab($tab, noteContext);
|
||||||
|
@ -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. */
|
/* #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,
|
||||||
body.layout-horizontal .tab-row-widget-container,
|
|
||||||
body.layout-horizontal .tab-row-container .note-tab[active] {
|
body.layout-horizontal .tab-row-container .note-tab[active] {
|
||||||
overflow: visible !important;
|
overflow: visible !important;
|
||||||
}
|
}
|
||||||
@ -905,10 +904,13 @@ body.layout-vertical.electron.platform-darwin .tab-row-container {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tab-row-widget-container {
|
.tab-row-widget-container {
|
||||||
margin-top: calc((var(--tab-bar-height) - var(--tab-height)) / 2);
|
|
||||||
height: var(--tab-height) !important;
|
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 {
|
body.layout-horizontal .tab-row-container {
|
||||||
padding-top: calc((var(--tab-bar-height) - var(--tab-height)));
|
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
|
/* Limit the drag area for the previous elements to include just to the element itself
|
||||||
and not its descendants also */
|
and not its descendants also */
|
||||||
body.layout-horizontal .tab-row-container > *,
|
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 > * {
|
body.layout-vertical #left-pane .quick-search > * {
|
||||||
-webkit-app-region: no-drag;
|
-webkit-app-region: no-drag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body.layout-horizontal .tab-row-widget,
|
||||||
body.layout-horizontal .tab-row-widget-container {
|
body.layout-horizontal .tab-row-widget-container {
|
||||||
margin-top: 0;
|
padding-top: 0;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user