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:
Evan Simkowitz 2024-12-11 18:25:36 -08:00 committed by GitHub
parent 9ce0093751
commit a9eeb55021
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 80 additions and 83 deletions

View File

@ -1,7 +1,13 @@
.iconbutton {
display: flex;
cursor: pointer;
opacity: 0.7;
align-items: center;
background: none;
border: none;
padding: 0;
font: inherit;
outline: inherit;
&.bulb {
color: var(--bulb-color);
@ -18,9 +24,9 @@
opacity: 1;
}
&.no-action {
cursor: default;
}
&.no-action {
cursor: default;
}
&.disabled {
cursor: default;

View File

@ -4,24 +4,27 @@
import { useLongClick } from "@/app/hook/useLongClick";
import { makeIconClass } from "@/util/util";
import clsx from "clsx";
import { memo, useRef } from "react";
import { forwardRef, memo, useRef } from "react";
import "./iconbutton.scss";
export const IconButton = memo(({ decl, className }: { decl: IconButtonDecl; className?: string }) => {
const buttonRef = useRef<HTMLDivElement>(null);
const spin = decl.iconSpin ?? false;
useLongClick(buttonRef, decl.click, decl.longClick, decl.disabled);
return (
<div
ref={buttonRef}
className={clsx("iconbutton", className, decl.className, {
disabled: decl.disabled,
"no-action": decl.noAction,
})}
title={decl.title}
style={{ color: decl.iconColor ?? "inherit" }}
>
{typeof decl.icon === "string" ? <i className={makeIconClass(decl.icon, true, { spin })} /> : decl.icon}
</div>
);
});
type IconButtonProps = { decl: IconButtonDecl; className?: string };
export const IconButton = memo(
forwardRef<HTMLButtonElement, IconButtonProps>(({ decl, className }, ref) => {
ref = ref ?? useRef<HTMLButtonElement>(null);
const spin = decl.iconSpin ?? false;
useLongClick(ref, decl.click, decl.longClick, decl.disabled);
return (
<button
ref={ref}
className={clsx("iconbutton", className, decl.className, {
disabled: decl.disabled,
"no-action": decl.noAction,
})}
title={decl.title}
style={{ color: decl.iconColor ?? "inherit" }}
>
{typeof decl.icon === "string" ? <i className={makeIconClass(decl.icon, true, { spin })} /> : decl.icon}
</button>
);
})
);

View File

@ -23,6 +23,7 @@
}
.tab-bar-wrapper {
padding-top: 6px;
position: relative;
user-select: none;
display: flex;
@ -30,7 +31,7 @@
width: 100vw;
-webkit-app-region: drag;
* {
button {
-webkit-app-region: no-drag;
}
@ -40,11 +41,11 @@
}
.tab-bar {
margin-top: 6px;
position: relative; // Needed for absolute positioning of child tabs
display: flex;
flex-direction: row;
height: 27px;
-webkit-app-region: no-drag;
}
.pinned-tab-spacer {
@ -61,7 +62,7 @@
display: flex;
align-items: center;
justify-content: center;
padding: 6px 6px 0 0;
padding: 0 6px 0 0;
}
.app-menu-button {
@ -77,34 +78,24 @@
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 {
height: 80%;
margin: auto 4px;
color: black;
padding: 0 6px;
flex: 0 0 fit-content;
}
.add-tab-btn {
width: 22px;
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;
}
.add-tab {
padding: 0 10px;
}
.window-drag {

View File

@ -11,6 +11,7 @@ import { useAtomValue } from "jotai";
import { OverlayScrollbars } from "overlayscrollbars";
import { createRef, memo, useCallback, useEffect, useRef, useState } from "react";
import { debounce } from "throttle-debounce";
import { IconButton } from "../element/iconbutton";
import { WorkspaceService } from "../store/services";
import { Tab } from "./tab";
import "./tabbar.scss";
@ -147,7 +148,7 @@ const TabBar = memo(({ workspace }: TabBarProps) => {
const tabBarRef = useRef<HTMLDivElement>(null);
const tabsWrapperRef = useRef<HTMLDivElement>(null);
const tabRefs = useRef<React.RefObject<HTMLDivElement>[]>([]);
const addBtnRef = useRef<HTMLDivElement>(null);
const addBtnRef = useRef<HTMLButtonElement>(null);
const draggingRemovedRef = useRef(false);
const draggingTabDataRef = useRef({
tabId: "",
@ -160,14 +161,13 @@ const TabBar = memo(({ workspace }: TabBarProps) => {
dragged: false,
});
const osInstanceRef = useRef<OverlayScrollbars>(null);
const draggerRightRef = useRef<HTMLDivElement>(null);
const draggerLeftRef = useRef<HTMLDivElement>(null);
const workspaceSwitcherRef = useRef<HTMLDivElement>(null);
const devLabelRef = useRef<HTMLDivElement>(null);
const appMenuButtonRef = useRef<HTMLDivElement>(null);
const tabWidthRef = useRef<number>(TAB_DEFAULT_WIDTH);
const scrollableRef = useRef<boolean>(false);
const updateStatusBannerRef = useRef<HTMLDivElement>(null);
const updateStatusBannerRef = useRef<HTMLButtonElement>(null);
const configErrorButtonRef = useRef<HTMLElement>(null);
const prevAllLoadedRef = useRef<boolean>(false);
const activeTabId = useAtomValue(atoms.staticTabId);
@ -226,7 +226,6 @@ const TabBar = memo(({ workspace }: TabBarProps) => {
const tabbarWrapperWidth = tabbarWrapperRef.current.getBoundingClientRect().width;
const windowDragLeftWidth = draggerLeftRef.current.getBoundingClientRect().width;
const windowDragRightWidth = draggerRightRef.current.getBoundingClientRect().width;
const addBtnWidth = addBtnRef.current.getBoundingClientRect().width;
const updateStatusLabelWidth = updateStatusBannerRef.current?.getBoundingClientRect().width ?? 0;
const configErrorWidth = configErrorButtonRef.current?.getBoundingClientRect().width ?? 0;
@ -236,7 +235,6 @@ const TabBar = memo(({ workspace }: TabBarProps) => {
const nonTabElementsWidth =
windowDragLeftWidth +
windowDragRightWidth +
addBtnWidth +
updateStatusLabelWidth +
configErrorWidth +
@ -648,6 +646,13 @@ const TabBar = memo(({ workspace }: TabBarProps) => {
<i className="fa fa-ellipsis" />
</div>
) : undefined;
const addtabButtonDecl: IconButtonDecl = {
elemtype: "iconbutton",
icon: "plus",
click: handleAddTab,
title: "Add Tab",
};
return (
<div ref={tabbarWrapperRef} className="tab-bar-wrapper">
<WindowDrag ref={draggerLeftRef} className="left" />
@ -680,12 +685,11 @@ const TabBar = memo(({ workspace }: TabBarProps) => {
})}
</div>
</div>
<div ref={addBtnRef} className="add-tab-btn" onClick={handleAddTab}>
<i className="fa fa-solid fa-plus fa-fw" />
<IconButton className="add-tab" ref={addBtnRef} decl={addtabButtonDecl} />
<div className="tab-bar-right">
<UpdateStatusBanner ref={updateStatusBannerRef} />
<ConfigErrorIcon buttonRef={configErrorButtonRef} />
</div>
<WindowDrag ref={draggerRightRef} className="right" />
<UpdateStatusBanner ref={updateStatusBannerRef} />
<ConfigErrorIcon buttonRef={configErrorButtonRef} />
</div>
);
});

View File

@ -1,15 +1,10 @@
.update-available-banner {
display: flex;
height: 100%;
.button {
color: black;
height: 80%;
margin: auto 4px;
background-color: var(--accent-color);
flex: 0 0 fit-content;
&:disabled {
opacity: unset !important;
}
.button {
color: black;
background-color: var(--accent-color);
flex: 0 0 fit-content;
line-height: unset !important;
padding: 0 6px;
&:disabled {
opacity: unset !important;
}
}

View File

@ -4,7 +4,7 @@ import { useAtomValue } from "jotai";
import { forwardRef, memo, useEffect, useState } from "react";
import "./updatebanner.scss";
const UpdateStatusBannerComponent = forwardRef<HTMLDivElement>((_, ref) => {
const UpdateStatusBannerComponent = forwardRef<HTMLButtonElement>((_, ref) => {
const appUpdateStatus = useAtomValue(atoms.updaterStatusAtom);
let [updateStatusMessage, setUpdateStatusMessage] = useState<string>();
const [dismissBannerTimeout, setDismissBannerTimeout] = useState<NodeJS.Timeout>();
@ -54,16 +54,15 @@ const UpdateStatusBannerComponent = forwardRef<HTMLDivElement>((_, ref) => {
}
if (updateStatusMessage) {
return (
<div className="update-available-banner" ref={ref}>
<Button
className="update-available-button"
title={appUpdateStatus === "ready" ? "Click to Install Update" : updateStatusMessage}
onClick={onClick}
disabled={appUpdateStatus !== "ready"}
>
{updateStatusMessage}
</Button>
</div>
<Button
className="update-available-banner"
title={appUpdateStatus === "ready" ? "Click to Install Update" : updateStatusMessage}
onClick={onClick}
disabled={appUpdateStatus !== "ready"}
ref={ref}
>
{updateStatusMessage}
</Button>
);
}
});

View File

@ -9,7 +9,6 @@
align-items: center;
gap: 12px;
border-radius: 6px;
margin-top: 6px;
margin-right: 13px;
box-sizing: border-box;
background-color: rgb(from var(--main-text-color) r g b / 0.1) !important;