feat: middle-click to close tabs on desktop (#232)
Add useMiddleClickClose hook that listens for auxclick (button === 1) events on web, enabling middle-click-to-close behavior on tab chips.
This commit is contained in:
@@ -1,4 +1,12 @@
|
|||||||
import { useCallback, useMemo, useState, type Dispatch, type SetStateAction } from "react";
|
import {
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useMemo,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
type Dispatch,
|
||||||
|
type SetStateAction,
|
||||||
|
} from "react";
|
||||||
import {
|
import {
|
||||||
ActivityIndicator,
|
ActivityIndicator,
|
||||||
Platform,
|
Platform,
|
||||||
@@ -101,6 +109,27 @@ function getFallbackTabLabel(tab: WorkspaceTabDescriptor): string {
|
|||||||
return "Agent";
|
return "Agent";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function useMiddleClickClose(onClose: () => void) {
|
||||||
|
const ref = useRef<View>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const node = ref.current as unknown as HTMLElement | null;
|
||||||
|
if (!node) return;
|
||||||
|
|
||||||
|
function handleAuxClick(event: MouseEvent) {
|
||||||
|
if (event.button === 1) {
|
||||||
|
event.preventDefault();
|
||||||
|
onClose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
node.addEventListener("auxclick", handleAuxClick);
|
||||||
|
return () => node.removeEventListener("auxclick", handleAuxClick);
|
||||||
|
}, [onClose]);
|
||||||
|
|
||||||
|
return ref;
|
||||||
|
}
|
||||||
|
|
||||||
function TabChip({
|
function TabChip({
|
||||||
tab,
|
tab,
|
||||||
isActive,
|
isActive,
|
||||||
@@ -140,6 +169,9 @@ function TabChip({
|
|||||||
}) {
|
}) {
|
||||||
const { theme } = useUnistyles();
|
const { theme } = useUnistyles();
|
||||||
const { closeButtonTestId, contextMenuTestId, menuEntries } = resolvedTab;
|
const { closeButtonTestId, contextMenuTestId, menuEntries } = resolvedTab;
|
||||||
|
const middleClickRef = useMiddleClickClose(
|
||||||
|
useCallback(() => void onCloseTab(tab.tabId), [onCloseTab, tab.tabId]),
|
||||||
|
);
|
||||||
const [hovered, setHovered] = useState(false);
|
const [hovered, setHovered] = useState(false);
|
||||||
const isHighlighted = isActive || hovered || isCloseHovered;
|
const isHighlighted = isActive || hovered || isCloseHovered;
|
||||||
const closeButtonDragBlockers =
|
const closeButtonDragBlockers =
|
||||||
@@ -155,6 +187,7 @@ function TabChip({
|
|||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<View ref={middleClickRef}>
|
||||||
<ContextMenu key={tab.key}>
|
<ContextMenu key={tab.key}>
|
||||||
<Tooltip delayDuration={400} enabledOnDesktop enabledOnMobile={false}>
|
<Tooltip delayDuration={400} enabledOnDesktop enabledOnMobile={false}>
|
||||||
<TooltipTrigger asChild triggerRefProp="triggerRef">
|
<TooltipTrigger asChild triggerRefProp="triggerRef">
|
||||||
@@ -325,6 +358,7 @@ function TabChip({
|
|||||||
)}
|
)}
|
||||||
</ContextMenuContent>
|
</ContextMenuContent>
|
||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user