mirror of
https://github.com/wavetermdev/waveterm.git
synced 2024-12-21 16:38:23 +01:00
Fix inconsistent tab sizing between views (#1502)
The windowdrag right spacer was acting erratic. It's also not necessary, since we can just use margins to let the banner buttons fill empty space so they float to the right side of the tab bar. Without it, though, I had to add more padding for the add tab button so it has more room.
This commit is contained in:
parent
9ce0093751
commit
a9eeb55021
@ -1,7 +1,13 @@
|
|||||||
.iconbutton {
|
.iconbutton {
|
||||||
|
display: flex;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
font: inherit;
|
||||||
|
outline: inherit;
|
||||||
|
|
||||||
&.bulb {
|
&.bulb {
|
||||||
color: var(--bulb-color);
|
color: var(--bulb-color);
|
||||||
|
@ -4,16 +4,18 @@
|
|||||||
import { useLongClick } from "@/app/hook/useLongClick";
|
import { useLongClick } from "@/app/hook/useLongClick";
|
||||||
import { makeIconClass } from "@/util/util";
|
import { makeIconClass } from "@/util/util";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import { memo, useRef } from "react";
|
import { forwardRef, memo, useRef } from "react";
|
||||||
import "./iconbutton.scss";
|
import "./iconbutton.scss";
|
||||||
|
|
||||||
export const IconButton = memo(({ decl, className }: { decl: IconButtonDecl; className?: string }) => {
|
type IconButtonProps = { decl: IconButtonDecl; className?: string };
|
||||||
const buttonRef = useRef<HTMLDivElement>(null);
|
export const IconButton = memo(
|
||||||
|
forwardRef<HTMLButtonElement, IconButtonProps>(({ decl, className }, ref) => {
|
||||||
|
ref = ref ?? useRef<HTMLButtonElement>(null);
|
||||||
const spin = decl.iconSpin ?? false;
|
const spin = decl.iconSpin ?? false;
|
||||||
useLongClick(buttonRef, decl.click, decl.longClick, decl.disabled);
|
useLongClick(ref, decl.click, decl.longClick, decl.disabled);
|
||||||
return (
|
return (
|
||||||
<div
|
<button
|
||||||
ref={buttonRef}
|
ref={ref}
|
||||||
className={clsx("iconbutton", className, decl.className, {
|
className={clsx("iconbutton", className, decl.className, {
|
||||||
disabled: decl.disabled,
|
disabled: decl.disabled,
|
||||||
"no-action": decl.noAction,
|
"no-action": decl.noAction,
|
||||||
@ -22,6 +24,7 @@ export const IconButton = memo(({ decl, className }: { decl: IconButtonDecl; cla
|
|||||||
style={{ color: decl.iconColor ?? "inherit" }}
|
style={{ color: decl.iconColor ?? "inherit" }}
|
||||||
>
|
>
|
||||||
{typeof decl.icon === "string" ? <i className={makeIconClass(decl.icon, true, { spin })} /> : decl.icon}
|
{typeof decl.icon === "string" ? <i className={makeIconClass(decl.icon, true, { spin })} /> : decl.icon}
|
||||||
</div>
|
</button>
|
||||||
|
);
|
||||||
|
})
|
||||||
);
|
);
|
||||||
});
|
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tab-bar-wrapper {
|
.tab-bar-wrapper {
|
||||||
|
padding-top: 6px;
|
||||||
position: relative;
|
position: relative;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -30,7 +31,7 @@
|
|||||||
width: 100vw;
|
width: 100vw;
|
||||||
-webkit-app-region: drag;
|
-webkit-app-region: drag;
|
||||||
|
|
||||||
* {
|
button {
|
||||||
-webkit-app-region: no-drag;
|
-webkit-app-region: no-drag;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,11 +41,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tab-bar {
|
.tab-bar {
|
||||||
margin-top: 6px;
|
|
||||||
position: relative; // Needed for absolute positioning of child tabs
|
position: relative; // Needed for absolute positioning of child tabs
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
height: 27px;
|
height: 27px;
|
||||||
|
-webkit-app-region: no-drag;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pinned-tab-spacer {
|
.pinned-tab-spacer {
|
||||||
@ -61,7 +62,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 6px 6px 0 0;
|
padding: 0 6px 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-menu-button {
|
.app-menu-button {
|
||||||
@ -77,34 +78,24 @@
|
|||||||
color: var(--accent-color);
|
color: var(--accent-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tab-bar-right {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 6px;
|
||||||
|
&:not(:empty) {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.config-error-button {
|
.config-error-button {
|
||||||
height: 80%;
|
|
||||||
margin: auto 4px;
|
|
||||||
color: black;
|
color: black;
|
||||||
|
padding: 0 6px;
|
||||||
flex: 0 0 fit-content;
|
flex: 0 0 fit-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
.add-tab-btn {
|
.add-tab {
|
||||||
width: 22px;
|
padding: 0 10px;
|
||||||
height: 100%;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 14px;
|
|
||||||
text-align: center;
|
|
||||||
user-select: none;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
opacity: 0.5;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
i {
|
|
||||||
overflow: hidden;
|
|
||||||
margin-top: 5px;
|
|
||||||
font-size: 11px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.window-drag {
|
.window-drag {
|
||||||
|
@ -11,6 +11,7 @@ import { useAtomValue } from "jotai";
|
|||||||
import { OverlayScrollbars } from "overlayscrollbars";
|
import { OverlayScrollbars } from "overlayscrollbars";
|
||||||
import { createRef, memo, useCallback, useEffect, useRef, useState } from "react";
|
import { createRef, memo, useCallback, useEffect, useRef, useState } from "react";
|
||||||
import { debounce } from "throttle-debounce";
|
import { debounce } from "throttle-debounce";
|
||||||
|
import { IconButton } from "../element/iconbutton";
|
||||||
import { WorkspaceService } from "../store/services";
|
import { WorkspaceService } from "../store/services";
|
||||||
import { Tab } from "./tab";
|
import { Tab } from "./tab";
|
||||||
import "./tabbar.scss";
|
import "./tabbar.scss";
|
||||||
@ -147,7 +148,7 @@ const TabBar = memo(({ workspace }: TabBarProps) => {
|
|||||||
const tabBarRef = useRef<HTMLDivElement>(null);
|
const tabBarRef = useRef<HTMLDivElement>(null);
|
||||||
const tabsWrapperRef = useRef<HTMLDivElement>(null);
|
const tabsWrapperRef = useRef<HTMLDivElement>(null);
|
||||||
const tabRefs = useRef<React.RefObject<HTMLDivElement>[]>([]);
|
const tabRefs = useRef<React.RefObject<HTMLDivElement>[]>([]);
|
||||||
const addBtnRef = useRef<HTMLDivElement>(null);
|
const addBtnRef = useRef<HTMLButtonElement>(null);
|
||||||
const draggingRemovedRef = useRef(false);
|
const draggingRemovedRef = useRef(false);
|
||||||
const draggingTabDataRef = useRef({
|
const draggingTabDataRef = useRef({
|
||||||
tabId: "",
|
tabId: "",
|
||||||
@ -160,14 +161,13 @@ const TabBar = memo(({ workspace }: TabBarProps) => {
|
|||||||
dragged: false,
|
dragged: false,
|
||||||
});
|
});
|
||||||
const osInstanceRef = useRef<OverlayScrollbars>(null);
|
const osInstanceRef = useRef<OverlayScrollbars>(null);
|
||||||
const draggerRightRef = useRef<HTMLDivElement>(null);
|
|
||||||
const draggerLeftRef = useRef<HTMLDivElement>(null);
|
const draggerLeftRef = useRef<HTMLDivElement>(null);
|
||||||
const workspaceSwitcherRef = useRef<HTMLDivElement>(null);
|
const workspaceSwitcherRef = useRef<HTMLDivElement>(null);
|
||||||
const devLabelRef = useRef<HTMLDivElement>(null);
|
const devLabelRef = useRef<HTMLDivElement>(null);
|
||||||
const appMenuButtonRef = useRef<HTMLDivElement>(null);
|
const appMenuButtonRef = useRef<HTMLDivElement>(null);
|
||||||
const tabWidthRef = useRef<number>(TAB_DEFAULT_WIDTH);
|
const tabWidthRef = useRef<number>(TAB_DEFAULT_WIDTH);
|
||||||
const scrollableRef = useRef<boolean>(false);
|
const scrollableRef = useRef<boolean>(false);
|
||||||
const updateStatusBannerRef = useRef<HTMLDivElement>(null);
|
const updateStatusBannerRef = useRef<HTMLButtonElement>(null);
|
||||||
const configErrorButtonRef = useRef<HTMLElement>(null);
|
const configErrorButtonRef = useRef<HTMLElement>(null);
|
||||||
const prevAllLoadedRef = useRef<boolean>(false);
|
const prevAllLoadedRef = useRef<boolean>(false);
|
||||||
const activeTabId = useAtomValue(atoms.staticTabId);
|
const activeTabId = useAtomValue(atoms.staticTabId);
|
||||||
@ -226,7 +226,6 @@ const TabBar = memo(({ workspace }: TabBarProps) => {
|
|||||||
|
|
||||||
const tabbarWrapperWidth = tabbarWrapperRef.current.getBoundingClientRect().width;
|
const tabbarWrapperWidth = tabbarWrapperRef.current.getBoundingClientRect().width;
|
||||||
const windowDragLeftWidth = draggerLeftRef.current.getBoundingClientRect().width;
|
const windowDragLeftWidth = draggerLeftRef.current.getBoundingClientRect().width;
|
||||||
const windowDragRightWidth = draggerRightRef.current.getBoundingClientRect().width;
|
|
||||||
const addBtnWidth = addBtnRef.current.getBoundingClientRect().width;
|
const addBtnWidth = addBtnRef.current.getBoundingClientRect().width;
|
||||||
const updateStatusLabelWidth = updateStatusBannerRef.current?.getBoundingClientRect().width ?? 0;
|
const updateStatusLabelWidth = updateStatusBannerRef.current?.getBoundingClientRect().width ?? 0;
|
||||||
const configErrorWidth = configErrorButtonRef.current?.getBoundingClientRect().width ?? 0;
|
const configErrorWidth = configErrorButtonRef.current?.getBoundingClientRect().width ?? 0;
|
||||||
@ -236,7 +235,6 @@ const TabBar = memo(({ workspace }: TabBarProps) => {
|
|||||||
|
|
||||||
const nonTabElementsWidth =
|
const nonTabElementsWidth =
|
||||||
windowDragLeftWidth +
|
windowDragLeftWidth +
|
||||||
windowDragRightWidth +
|
|
||||||
addBtnWidth +
|
addBtnWidth +
|
||||||
updateStatusLabelWidth +
|
updateStatusLabelWidth +
|
||||||
configErrorWidth +
|
configErrorWidth +
|
||||||
@ -648,6 +646,13 @@ const TabBar = memo(({ workspace }: TabBarProps) => {
|
|||||||
<i className="fa fa-ellipsis" />
|
<i className="fa fa-ellipsis" />
|
||||||
</div>
|
</div>
|
||||||
) : undefined;
|
) : undefined;
|
||||||
|
|
||||||
|
const addtabButtonDecl: IconButtonDecl = {
|
||||||
|
elemtype: "iconbutton",
|
||||||
|
icon: "plus",
|
||||||
|
click: handleAddTab,
|
||||||
|
title: "Add Tab",
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<div ref={tabbarWrapperRef} className="tab-bar-wrapper">
|
<div ref={tabbarWrapperRef} className="tab-bar-wrapper">
|
||||||
<WindowDrag ref={draggerLeftRef} className="left" />
|
<WindowDrag ref={draggerLeftRef} className="left" />
|
||||||
@ -680,13 +685,12 @@ const TabBar = memo(({ workspace }: TabBarProps) => {
|
|||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div ref={addBtnRef} className="add-tab-btn" onClick={handleAddTab}>
|
<IconButton className="add-tab" ref={addBtnRef} decl={addtabButtonDecl} />
|
||||||
<i className="fa fa-solid fa-plus fa-fw" />
|
<div className="tab-bar-right">
|
||||||
</div>
|
|
||||||
<WindowDrag ref={draggerRightRef} className="right" />
|
|
||||||
<UpdateStatusBanner ref={updateStatusBannerRef} />
|
<UpdateStatusBanner ref={updateStatusBannerRef} />
|
||||||
<ConfigErrorIcon buttonRef={configErrorButtonRef} />
|
<ConfigErrorIcon buttonRef={configErrorButtonRef} />
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,15 +1,10 @@
|
|||||||
.update-available-banner {
|
|
||||||
display: flex;
|
|
||||||
height: 100%;
|
|
||||||
.button {
|
.button {
|
||||||
color: black;
|
color: black;
|
||||||
height: 80%;
|
|
||||||
margin: auto 4px;
|
|
||||||
background-color: var(--accent-color);
|
background-color: var(--accent-color);
|
||||||
flex: 0 0 fit-content;
|
flex: 0 0 fit-content;
|
||||||
|
line-height: unset !important;
|
||||||
|
padding: 0 6px;
|
||||||
&:disabled {
|
&:disabled {
|
||||||
opacity: unset !important;
|
opacity: unset !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -4,7 +4,7 @@ import { useAtomValue } from "jotai";
|
|||||||
import { forwardRef, memo, useEffect, useState } from "react";
|
import { forwardRef, memo, useEffect, useState } from "react";
|
||||||
import "./updatebanner.scss";
|
import "./updatebanner.scss";
|
||||||
|
|
||||||
const UpdateStatusBannerComponent = forwardRef<HTMLDivElement>((_, ref) => {
|
const UpdateStatusBannerComponent = forwardRef<HTMLButtonElement>((_, ref) => {
|
||||||
const appUpdateStatus = useAtomValue(atoms.updaterStatusAtom);
|
const appUpdateStatus = useAtomValue(atoms.updaterStatusAtom);
|
||||||
let [updateStatusMessage, setUpdateStatusMessage] = useState<string>();
|
let [updateStatusMessage, setUpdateStatusMessage] = useState<string>();
|
||||||
const [dismissBannerTimeout, setDismissBannerTimeout] = useState<NodeJS.Timeout>();
|
const [dismissBannerTimeout, setDismissBannerTimeout] = useState<NodeJS.Timeout>();
|
||||||
@ -54,16 +54,15 @@ const UpdateStatusBannerComponent = forwardRef<HTMLDivElement>((_, ref) => {
|
|||||||
}
|
}
|
||||||
if (updateStatusMessage) {
|
if (updateStatusMessage) {
|
||||||
return (
|
return (
|
||||||
<div className="update-available-banner" ref={ref}>
|
|
||||||
<Button
|
<Button
|
||||||
className="update-available-button"
|
className="update-available-banner"
|
||||||
title={appUpdateStatus === "ready" ? "Click to Install Update" : updateStatusMessage}
|
title={appUpdateStatus === "ready" ? "Click to Install Update" : updateStatusMessage}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
disabled={appUpdateStatus !== "ready"}
|
disabled={appUpdateStatus !== "ready"}
|
||||||
|
ref={ref}
|
||||||
>
|
>
|
||||||
{updateStatusMessage}
|
{updateStatusMessage}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -9,7 +9,6 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
margin-top: 6px;
|
|
||||||
margin-right: 13px;
|
margin-right: 13px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
background-color: rgb(from var(--main-text-color) r g b / 0.1) !important;
|
background-color: rgb(from var(--main-text-color) r g b / 0.1) !important;
|
||||||
|
Loading…
Reference in New Issue
Block a user