mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-01-19 21:11:32 +01:00
Move tab bar to top edge (#72)
This commit is contained in:
parent
484d58b88d
commit
4714b88be7
@ -51,13 +51,6 @@ body {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.titlebar {
|
|
||||||
height: 35px;
|
|
||||||
border-bottom: 1px solid var(--border-color);
|
|
||||||
flex-shrink: 0;
|
|
||||||
-webkit-app-region: drag;
|
|
||||||
}
|
|
||||||
|
|
||||||
.error-boundary {
|
.error-boundary {
|
||||||
color: var(--error-color);
|
color: var(--error-color);
|
||||||
}
|
}
|
||||||
|
@ -192,7 +192,6 @@ const AppInner = () => {
|
|||||||
if (client == null || windowData == null) {
|
if (client == null || windowData == null) {
|
||||||
return (
|
return (
|
||||||
<div className="mainapp">
|
<div className="mainapp">
|
||||||
<div className="titlebar"></div>
|
|
||||||
<CenteredDiv>invalid configuration, client or window was not loaded</CenteredDiv>
|
<CenteredDiv>invalid configuration, client or window was not loaded</CenteredDiv>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -243,7 +242,6 @@ const AppInner = () => {
|
|||||||
return (
|
return (
|
||||||
<div className="mainapp" onContextMenu={handleContextMenu}>
|
<div className="mainapp" onContextMenu={handleContextMenu}>
|
||||||
<DndProvider backend={HTML5Backend}>
|
<DndProvider backend={HTML5Backend}>
|
||||||
<div className="titlebar"></div>
|
|
||||||
<Workspace />
|
<Workspace />
|
||||||
</DndProvider>
|
</DndProvider>
|
||||||
</div>
|
</div>
|
||||||
|
7
frontend/app/element/windowdrag.less
Normal file
7
frontend/app/element/windowdrag.less
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
// Copyright 2024, Command Line Inc.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
.window-drag {
|
||||||
|
-webkit-app-region: drag;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
22
frontend/app/element/windowdrag.tsx
Normal file
22
frontend/app/element/windowdrag.tsx
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// Copyright 2024, Command Line Inc.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
import { clsx } from "clsx";
|
||||||
|
import React, { forwardRef } from "react";
|
||||||
|
|
||||||
|
import "./windowdrag.less";
|
||||||
|
|
||||||
|
interface WindowDragProps {
|
||||||
|
className?: string;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const WindowDrag = forwardRef<HTMLDivElement, WindowDragProps>(({ children, className }, ref) => {
|
||||||
|
return (
|
||||||
|
<div ref={ref} className={clsx(`window-drag`, className)}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export { WindowDrag };
|
@ -53,6 +53,10 @@
|
|||||||
background-color: var(--border-color);
|
background-color: var(--border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.vertical-line.first {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.close {
|
.close {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
|
// Copyright 2024, Command Line Inc.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
import { Button } from "@/element/button";
|
import { Button } from "@/element/button";
|
||||||
import { ContextMenuModel } from "@/store/contextmenu";
|
|
||||||
import * as services from "@/store/services";
|
import * as services from "@/store/services";
|
||||||
import * as WOS from "@/store/wos";
|
import * as WOS from "@/store/wos";
|
||||||
import { clsx } from "clsx";
|
import { clsx } from "clsx";
|
||||||
@ -10,16 +12,17 @@ import "./tab.less";
|
|||||||
interface TabProps {
|
interface TabProps {
|
||||||
id: string;
|
id: string;
|
||||||
active: boolean;
|
active: boolean;
|
||||||
|
isFirst: boolean;
|
||||||
isBeforeActive: boolean;
|
isBeforeActive: boolean;
|
||||||
isDragging: boolean;
|
isDragging: boolean;
|
||||||
onSelect: () => void;
|
onSelect: () => void;
|
||||||
onClose: (event: React.MouseEvent<HTMLElement, MouseEvent>) => void;
|
onClose: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
|
||||||
onDragStart: (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => void;
|
onDragStart: (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => void;
|
||||||
onLoaded: () => void;
|
onLoaded: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Tab = forwardRef<HTMLDivElement, TabProps>(
|
const Tab = forwardRef<HTMLDivElement, TabProps>(
|
||||||
({ id, active, isBeforeActive, isDragging, onLoaded, onSelect, onClose, onDragStart }, ref) => {
|
({ id, active, isFirst, isBeforeActive, isDragging, onLoaded, onSelect, onClose, onDragStart }, ref) => {
|
||||||
const [tabData, tabLoading] = WOS.useWaveObjectValue<Tab>(WOS.makeORef("tab", id));
|
const [tabData, tabLoading] = WOS.useWaveObjectValue<Tab>(WOS.makeORef("tab", id));
|
||||||
const [originalName, setOriginalName] = useState("");
|
const [originalName, setOriginalName] = useState("");
|
||||||
const [isEditable, setIsEditable] = useState(false);
|
const [isEditable, setIsEditable] = useState(false);
|
||||||
@ -42,10 +45,8 @@ const Tab = forwardRef<HTMLDivElement, TabProps>(
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleDoubleClick = (event?: React.MouseEvent<any, any>) => {
|
const handleDoubleClick = (event) => {
|
||||||
if (event != null) {
|
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
}
|
|
||||||
setIsEditable(true);
|
setIsEditable(true);
|
||||||
editableTimeoutRef.current = setTimeout(() => {
|
editableTimeoutRef.current = setTimeout(() => {
|
||||||
if (editableRef.current) {
|
if (editableRef.current) {
|
||||||
@ -105,35 +106,15 @@ const Tab = forwardRef<HTMLDivElement, TabProps>(
|
|||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
};
|
};
|
||||||
|
|
||||||
function handleContextMenu(e: React.MouseEvent<HTMLElement>) {
|
|
||||||
let menu: ContextMenuItem[] = [];
|
|
||||||
menu.push({
|
|
||||||
label: "Edit Name",
|
|
||||||
click: () => {
|
|
||||||
handleDoubleClick(null);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
menu.push({
|
|
||||||
type: "separator",
|
|
||||||
});
|
|
||||||
menu.push({
|
|
||||||
label: "Close",
|
|
||||||
click: () => {
|
|
||||||
onClose(e);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
ContextMenuModel.showContextMenu(menu, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={clsx("tab", { active, isDragging, "before-active": isBeforeActive })}
|
className={clsx("tab", { active, isDragging, "before-active": isBeforeActive })}
|
||||||
onMouseDown={onDragStart}
|
onMouseDown={onDragStart}
|
||||||
onClick={onSelect}
|
onClick={onSelect}
|
||||||
onContextMenu={handleContextMenu}
|
|
||||||
data-tab-id={id}
|
data-tab-id={id}
|
||||||
>
|
>
|
||||||
|
{isFirst && <div className="vertical-line first" />}
|
||||||
<div
|
<div
|
||||||
ref={editableRef}
|
ref={editableRef}
|
||||||
className={clsx("name", { focused: isEditable })}
|
className={clsx("name", { focused: isEditable })}
|
||||||
|
@ -4,21 +4,25 @@
|
|||||||
.tab-bar-wrapper {
|
.tab-bar-wrapper {
|
||||||
position: relative;
|
position: relative;
|
||||||
border-bottom: 1px solid var(--border-color);
|
border-bottom: 1px solid var(--border-color);
|
||||||
-webkit-user-select: none;
|
user-select: none;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
.tab-bar {
|
.tab-bar {
|
||||||
position: relative; // Needed for absolute positioning of child tabs
|
position: relative; // Needed for absolute positioning of child tabs
|
||||||
min-height: 34px; // Adjust as necessary to fit the height of tabs
|
margin-left: 100px;
|
||||||
width: calc(100vw - 36px); // 36 is the width of add tab button
|
height: 34px;
|
||||||
|
// 36 is the width of add tab button
|
||||||
|
// 100 is offset from the left, for macOS window controls and dragging
|
||||||
|
// 50 right offset for dragging
|
||||||
|
// minus 1px for last tab right border
|
||||||
|
width: calc(100vw - 185px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.add-tab-btn {
|
.add-tab-btn {
|
||||||
width: 36px;
|
width: 36px;
|
||||||
height: 34px;
|
height: 100%;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%); // overridden in js
|
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
@ -28,15 +32,24 @@
|
|||||||
background-color: var(--main-bg-color);
|
background-color: var(--main-bg-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.window-drag {
|
||||||
|
position: absolute;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
&.left {
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Customize scrollbar styles
|
// Customize scrollbar styles
|
||||||
.os-theme-dark,
|
.os-theme-dark,
|
||||||
.os-theme-light {
|
.os-theme-light {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
--os-size: 3px;
|
--os-size: 2px;
|
||||||
--os-padding-perpendicular: 0px;
|
--os-padding-perpendicular: 0px;
|
||||||
--os-padding-axis: 0px;
|
--os-padding-axis: 0px;
|
||||||
--os-track-border-radius: 3px;
|
--os-track-border-radius: 2px;
|
||||||
--os-handle-interactive-area-offset: 0px;
|
--os-handle-interactive-area-offset: 0px;
|
||||||
--os-handle-border-radius: 3px;
|
--os-handle-border-radius: 2px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright 2024, Command Line Inc.
|
// Copyright 2024, Command Line Inc.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
import { WindowDrag } from "@/element/windowdrag";
|
||||||
import { deleteLayoutStateAtomForTab } from "@/faraday/lib/layoutAtom";
|
import { deleteLayoutStateAtomForTab } from "@/faraday/lib/layoutAtom";
|
||||||
import { debounce } from "@/faraday/lib/utils";
|
import { debounce } from "@/faraday/lib/utils";
|
||||||
import { atoms } from "@/store/global";
|
import { atoms } from "@/store/global";
|
||||||
@ -41,8 +42,8 @@ const TabBar = ({ workspace }: TabBarProps) => {
|
|||||||
const [dragStartPositions, setDragStartPositions] = useState<number[]>([]);
|
const [dragStartPositions, setDragStartPositions] = useState<number[]>([]);
|
||||||
const [draggingTab, setDraggingTab] = useState<string>();
|
const [draggingTab, setDraggingTab] = useState<string>();
|
||||||
const [tabsLoaded, setTabsLoaded] = useState({});
|
const [tabsLoaded, setTabsLoaded] = useState({});
|
||||||
const [scrollable, setScrollable] = useState(false);
|
// const [scrollable, setScrollable] = useState(false);
|
||||||
const [tabWidth, setTabWidth] = useState(TAB_DEFAULT_WIDTH);
|
// const [tabWidth, setTabWidth] = useState(TAB_DEFAULT_WIDTH);
|
||||||
|
|
||||||
const tabBarRef = useRef<HTMLDivElement>(null);
|
const tabBarRef = useRef<HTMLDivElement>(null);
|
||||||
const tabsWrapperRef = useRef<HTMLDivElement>(null);
|
const tabsWrapperRef = useRef<HTMLDivElement>(null);
|
||||||
@ -61,6 +62,9 @@ const TabBar = ({ workspace }: TabBarProps) => {
|
|||||||
dragged: false,
|
dragged: false,
|
||||||
});
|
});
|
||||||
const osInstanceRef = useRef<OverlayScrollbars>(null);
|
const osInstanceRef = useRef<OverlayScrollbars>(null);
|
||||||
|
const draggerRightRef = useRef<HTMLDivElement>(null);
|
||||||
|
const tabWidthRef = useRef<number>(TAB_DEFAULT_WIDTH);
|
||||||
|
const scrollableRef = useRef<boolean>(false);
|
||||||
|
|
||||||
const windowData = useAtomValue(atoms.waveWindow);
|
const windowData = useAtomValue(atoms.waveWindow);
|
||||||
const { activetabid } = windowData;
|
const { activetabid } = windowData;
|
||||||
@ -105,18 +109,21 @@ const TabBar = ({ workspace }: TabBarProps) => {
|
|||||||
setDragStartPositions(newStartPositions);
|
setDragStartPositions(newStartPositions);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const debouncedSetTabWidth = debounce((width) => setTabWidth(width), 100);
|
// const debouncedSetTabWidth = debounce((width) => setTabWidth(width), 100);
|
||||||
const debouncedSetScrollable = debounce((scrollable) => setScrollable(scrollable), 100);
|
// const debouncedSetScrollable = debounce((scrollable) => setScrollable(scrollable), 100);
|
||||||
const debouncedUpdateTabPositions = debounce(() => updateTabPositions(), 100);
|
const debouncedUpdateTabPositions = debounce(() => updateTabPositions(), 100);
|
||||||
|
|
||||||
const handleResizeTabs = useCallback(() => {
|
const handleResizeTabs = useCallback(() => {
|
||||||
const tabBar = tabBarRef.current;
|
const tabBar = tabBarRef.current;
|
||||||
if (tabBar === null) return;
|
if (tabBar === null) return;
|
||||||
|
|
||||||
const tabBarWidth = tabBar.getBoundingClientRect().width;
|
const tabBarRect = tabBar.getBoundingClientRect();
|
||||||
|
const tabBarWidth = tabBarRect.width;
|
||||||
const numberOfTabs = tabIds.length;
|
const numberOfTabs = tabIds.length;
|
||||||
const totalDefaultTabWidth = numberOfTabs * TAB_DEFAULT_WIDTH;
|
const totalDefaultTabWidth = numberOfTabs * TAB_DEFAULT_WIDTH;
|
||||||
const minTotalTabWidth = numberOfTabs * TAB_MIN_WIDTH;
|
const minTotalTabWidth = numberOfTabs * TAB_MIN_WIDTH;
|
||||||
|
const tabWidth = tabWidthRef.current;
|
||||||
|
const scrollable = scrollableRef.current;
|
||||||
let newTabWidth = tabWidth;
|
let newTabWidth = tabWidth;
|
||||||
let newScrollable = scrollable;
|
let newScrollable = scrollable;
|
||||||
|
|
||||||
@ -144,11 +151,11 @@ const TabBar = ({ workspace }: TabBarProps) => {
|
|||||||
|
|
||||||
// Update the state with the new tab width if it has changed
|
// Update the state with the new tab width if it has changed
|
||||||
if (newTabWidth !== tabWidth) {
|
if (newTabWidth !== tabWidth) {
|
||||||
debouncedSetTabWidth(newTabWidth);
|
tabWidthRef.current = newTabWidth;
|
||||||
}
|
}
|
||||||
// Update the state with the new scrollable state if it has changed
|
// Update the state with the new scrollable state if it has changed
|
||||||
if (newScrollable !== scrollable) {
|
if (newScrollable !== scrollable) {
|
||||||
debouncedSetScrollable(newScrollable);
|
scrollableRef.current = newScrollable;
|
||||||
}
|
}
|
||||||
// Initialize/destroy overlay scrollbars
|
// Initialize/destroy overlay scrollbars
|
||||||
if (newScrollable) {
|
if (newScrollable) {
|
||||||
@ -159,21 +166,29 @@ const TabBar = ({ workspace }: TabBarProps) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the position of the Add Tab button if needed
|
// Update Add Tab button position if needed
|
||||||
const addButton = addBtnRef.current;
|
const addButton = addBtnRef.current;
|
||||||
const lastTabRef = tabRefs.current[tabRefs.current.length - 1];
|
const lastTabRef = tabRefs.current[tabRefs.current.length - 1];
|
||||||
if (addButton && lastTabRef && lastTabRef.current) {
|
if (addButton && lastTabRef && lastTabRef.current) {
|
||||||
const lastTabRect = lastTabRef.current.getBoundingClientRect();
|
const lastTabRect = lastTabRef.current.getBoundingClientRect();
|
||||||
addButton.style.position = "absolute";
|
addButton.style.position = "absolute";
|
||||||
if (newScrollable) {
|
if (newScrollable) {
|
||||||
addButton.style.transform = `translateX(${document.documentElement.clientWidth - addButton.offsetWidth}px) translateY(-50%)`;
|
addButton.style.transform = `translateX(${tabBarRect.left + tabBarWidth + 1}px)`;
|
||||||
} else {
|
} else {
|
||||||
addButton.style.transform = `translateX(${lastTabRect.right + 1}px) translateY(-50%)`;
|
addButton.style.transform = `translateX(${lastTabRect.right + 1}px)`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Update dragger right position if needed
|
||||||
|
const draggerRight = draggerRightRef.current;
|
||||||
|
if (draggerRight && addButton) {
|
||||||
|
const addButtonRect = addButton.getBoundingClientRect();
|
||||||
|
const targetPos = addButtonRect.left + addButtonRect.width;
|
||||||
|
draggerRight.style.transform = `translateX(${targetPos}px)`;
|
||||||
|
draggerRight.style.width = `${document.documentElement.offsetWidth - targetPos}px`;
|
||||||
|
}
|
||||||
|
|
||||||
debouncedUpdateTabPositions();
|
debouncedUpdateTabPositions();
|
||||||
}, [tabIds, tabWidth, scrollable]);
|
}, [tabIds]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
window.addEventListener("resize", () => handleResizeTabs());
|
window.addEventListener("resize", () => handleResizeTabs());
|
||||||
@ -219,6 +234,7 @@ const TabBar = ({ workspace }: TabBarProps) => {
|
|||||||
|
|
||||||
const getNewTabIndex = (currentX: number, tabIndex: number, dragDirection: string) => {
|
const getNewTabIndex = (currentX: number, tabIndex: number, dragDirection: string) => {
|
||||||
let newTabIndex = tabIndex;
|
let newTabIndex = tabIndex;
|
||||||
|
const tabWidth = tabWidthRef.current;
|
||||||
if (dragDirection === "+") {
|
if (dragDirection === "+") {
|
||||||
// Dragging to the right
|
// Dragging to the right
|
||||||
for (let i = tabIndex + 1; i < tabIds.length; i++) {
|
for (let i = tabIndex + 1; i < tabIds.length; i++) {
|
||||||
@ -250,21 +266,26 @@ const TabBar = ({ workspace }: TabBarProps) => {
|
|||||||
}
|
}
|
||||||
let currentX = event.clientX - initialOffsetX - totalScrollOffset;
|
let currentX = event.clientX - initialOffsetX - totalScrollOffset;
|
||||||
let tabBarRectWidth = tabBarRef.current.getBoundingClientRect().width;
|
let tabBarRectWidth = tabBarRef.current.getBoundingClientRect().width;
|
||||||
|
// for macos, it's offset to make space for the window buttons
|
||||||
|
const tabBarRectLeftOffset = tabBarRef.current.getBoundingClientRect().left;
|
||||||
|
const incrementDecrement = tabBarRectLeftOffset * 0.05;
|
||||||
const dragDirection = getDragDirection(currentX);
|
const dragDirection = getDragDirection(currentX);
|
||||||
|
const scrollable = scrollableRef.current;
|
||||||
|
const tabWidth = tabWidthRef.current;
|
||||||
|
|
||||||
// Scroll the tab bar if the dragged tab overflows the container bounds
|
// Scroll the tab bar if the dragged tab overflows the container bounds
|
||||||
if (scrollable) {
|
if (scrollable) {
|
||||||
const { viewport } = osInstanceRef.current.elements();
|
const { viewport } = osInstanceRef.current.elements();
|
||||||
const currentScrollLeft = viewport.scrollLeft;
|
const currentScrollLeft = viewport.scrollLeft;
|
||||||
|
|
||||||
if (event.clientX <= 0) {
|
if (event.clientX <= tabBarRectLeftOffset) {
|
||||||
viewport.scrollLeft = Math.max(0, currentScrollLeft - 5); // Scroll left
|
viewport.scrollLeft = Math.max(0, currentScrollLeft - incrementDecrement); // Scroll left
|
||||||
if (viewport.scrollLeft !== currentScrollLeft) {
|
if (viewport.scrollLeft !== currentScrollLeft) {
|
||||||
// Only adjust if the scroll actually changed
|
// Only adjust if the scroll actually changed
|
||||||
draggingTabDataRef.current.totalScrollOffset += currentScrollLeft - viewport.scrollLeft;
|
draggingTabDataRef.current.totalScrollOffset += currentScrollLeft - viewport.scrollLeft;
|
||||||
}
|
}
|
||||||
} else if (event.clientX >= tabBarRectWidth) {
|
} else if (event.clientX >= tabBarRectWidth + tabBarRectLeftOffset) {
|
||||||
viewport.scrollLeft = Math.min(viewport.scrollWidth, currentScrollLeft + 5); // Scroll right
|
viewport.scrollLeft = Math.min(viewport.scrollWidth, currentScrollLeft + incrementDecrement); // Scroll right
|
||||||
if (viewport.scrollLeft !== currentScrollLeft) {
|
if (viewport.scrollLeft !== currentScrollLeft) {
|
||||||
// Only adjust if the scroll actually changed
|
// Only adjust if the scroll actually changed
|
||||||
draggingTabDataRef.current.totalScrollOffset -= viewport.scrollLeft - currentScrollLeft;
|
draggingTabDataRef.current.totalScrollOffset -= viewport.scrollLeft - currentScrollLeft;
|
||||||
@ -343,6 +364,7 @@ const TabBar = ({ workspace }: TabBarProps) => {
|
|||||||
|
|
||||||
// Update the final position of the dragged tab
|
// Update the final position of the dragged tab
|
||||||
const draggingTab = tabIds[tabIndex];
|
const draggingTab = tabIds[tabIndex];
|
||||||
|
const tabWidth = tabWidthRef.current;
|
||||||
const finalLeftPosition = tabIndex * tabWidth;
|
const finalLeftPosition = tabIndex * tabWidth;
|
||||||
const ref = tabRefs.current.find((ref) => ref.current.dataset.tabId === draggingTab);
|
const ref = tabRefs.current.find((ref) => ref.current.dataset.tabId === draggingTab);
|
||||||
if (ref.current) {
|
if (ref.current) {
|
||||||
@ -418,14 +440,14 @@ const TabBar = ({ workspace }: TabBarProps) => {
|
|||||||
services.ObjectService.AddTabToWorkspace(newTabName, true);
|
services.ObjectService.AddTabToWorkspace(newTabName, true);
|
||||||
|
|
||||||
scrollToNewTabTimeoutIdRef.current = setTimeout(() => {
|
scrollToNewTabTimeoutIdRef.current = setTimeout(() => {
|
||||||
if (scrollable) {
|
if (scrollableRef.current) {
|
||||||
const { viewport } = osInstanceRef.current.elements();
|
const { viewport } = osInstanceRef.current.elements();
|
||||||
viewport.scrollLeft = tabIds.length * tabWidth;
|
viewport.scrollLeft = tabIds.length * tabWidthRef.current;
|
||||||
}
|
}
|
||||||
}, 30);
|
}, 30);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCloseTab = (event: React.MouseEvent<HTMLElement, MouseEvent>, tabId: string) => {
|
const handleCloseTab = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>, tabId: string) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
services.WindowService.CloseTab(tabId);
|
services.WindowService.CloseTab(tabId);
|
||||||
deleteLayoutStateAtomForTab(tabId);
|
deleteLayoutStateAtomForTab(tabId);
|
||||||
@ -445,10 +467,11 @@ const TabBar = ({ workspace }: TabBarProps) => {
|
|||||||
return tabIds.indexOf(tabId) === tabIds.indexOf(activetabid) - 1;
|
return tabIds.indexOf(tabId) === tabIds.indexOf(activetabid) - 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
const tabsWrapperWidth = tabIds.length * tabWidth;
|
const tabsWrapperWidth = tabIds.length * tabWidthRef.current;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="tab-bar-wrapper">
|
<div className="tab-bar-wrapper">
|
||||||
|
<WindowDrag className="left" />
|
||||||
<div className="tab-bar" ref={tabBarRef} data-overlayscrollbars-initialize>
|
<div className="tab-bar" ref={tabBarRef} data-overlayscrollbars-initialize>
|
||||||
<div className="tabs-wrapper" ref={tabsWrapperRef} style={{ width: tabsWrapperWidth }}>
|
<div className="tabs-wrapper" ref={tabsWrapperRef} style={{ width: tabsWrapperWidth }}>
|
||||||
{tabIds.map((tabId, index) => (
|
{tabIds.map((tabId, index) => (
|
||||||
@ -456,6 +479,7 @@ const TabBar = ({ workspace }: TabBarProps) => {
|
|||||||
key={tabId}
|
key={tabId}
|
||||||
ref={tabRefs.current[index]}
|
ref={tabRefs.current[index]}
|
||||||
id={tabId}
|
id={tabId}
|
||||||
|
isFirst={index === 0}
|
||||||
onSelect={() => handleSelectTab(tabId)}
|
onSelect={() => handleSelectTab(tabId)}
|
||||||
active={activetabid === tabId}
|
active={activetabid === tabId}
|
||||||
onDragStart={(event) => handleDragStart(event, tabId, tabRefs.current[index])}
|
onDragStart={(event) => handleDragStart(event, tabId, tabRefs.current[index])}
|
||||||
@ -470,6 +494,7 @@ const TabBar = ({ workspace }: TabBarProps) => {
|
|||||||
<div ref={addBtnRef} className="add-tab-btn" onClick={handleAddTab}>
|
<div ref={addBtnRef} className="add-tab-btn" onClick={handleAddTab}>
|
||||||
<i className="fa fa-solid fa-plus fa-fw" />
|
<i className="fa fa-solid fa-plus fa-fw" />
|
||||||
</div>
|
</div>
|
||||||
|
<WindowDrag ref={draggerRightRef} className="right" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user