Skip to content

Commit 88c2abf

Browse files
committed
Handle link and tab middle click
1 parent fbb958f commit 88c2abf

1 file changed

Lines changed: 34 additions & 6 deletions

File tree

‎packages/ui/src/router.tsx‎

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -177,20 +177,45 @@ const useTabShortcuts = ({ baseRoute, runtime, tabsRx }: UseTabShortcutsProps) =
177177
}, [baseRoute, router, runtime, tabsRx]);
178178
};
179179

180+
// https://github.com/facebook/react/issues/29832#issuecomment-2490465022
181+
const updateRef = <T,>(ref: Ref<T> | undefined, node: T | undefined) => {
182+
if (!node) return;
183+
if (typeof ref === 'function') ref(node);
184+
else if (ref) ref.current = node;
185+
};
186+
180187
export interface UseLinkProps extends ActiveLinkOptions {
181188
children?: ((state: { isActive: boolean; isTransitioning: boolean }) => React.ReactNode) | React.ReactNode;
189+
onAuxClick?: () => void;
182190
ref?: Ref<unknown> | undefined;
183191
}
184192

185-
export const useLink = ({ children, ref, ...props }: UseLinkProps) => {
186-
const _ = useLinkProps(props, ref as Ref<Element>) as ComponentProps<'a'> & Record<string, unknown>;
193+
export const useLink = ({ children, onAuxClick, ref: refProp, ...props }: UseLinkProps) => {
194+
const _ = useLinkProps(props, refProp as Ref<Element>) as ComponentProps<'a'> & Record<string, unknown>;
187195

188196
const isActive = _['data-status'] === 'active';
189197
const isTransitioning = _['data-transitioning'] === 'transitioning';
190198

199+
const onAction = fauxEvent(_.onClick, { button: 0 });
200+
201+
const ref = (node: unknown) => {
202+
updateRef(_.ref, node as HTMLAnchorElement);
203+
204+
let element: HTMLElement | undefined;
205+
if (node && typeof node === 'object' && 'addEventListener' in node) element = node as HTMLElement;
206+
207+
const onAuxClickHandler = (event: MouseEvent) => {
208+
event.preventDefault();
209+
if (onAuxClick) onAuxClick();
210+
else onAction();
211+
};
212+
213+
element?.addEventListener('auxclick', onAuxClickHandler);
214+
return () => element?.removeEventListener('auxclick', onAuxClickHandler);
215+
};
216+
191217
return {
192-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
193-
ref: _.ref as Ref<any>,
218+
ref,
194219

195220
children: typeof children === 'function' ? children({ isActive, isTransitioning }) : children,
196221
href: _.href!,
@@ -199,7 +224,7 @@ export const useLink = ({ children, ref, ...props }: UseLinkProps) => {
199224
isDisabled: _['disabled'] === true,
200225
isTransitioning,
201226

202-
onAction: fauxEvent(_.onClick, { button: 0 }),
227+
onAction,
203228
onFocus: fauxEvent(_.onFocus),
204229
onHoverEnd: fauxEvent(_.onMouseLeave),
205230
onHoverStart: fauxEvent(_.onMouseEnter),
@@ -216,7 +241,10 @@ interface TabItemProps extends TabsRouteContext, ToOptions {
216241
const TabItem = ({ baseRoute, id, runtime, tab, tabsRx }: TabItemProps) => {
217242
const removeTab = useRemoveTab();
218243

219-
const { isActive, ...linkProps } = useLink(tab.route);
244+
const { isActive, ...linkProps } = useLink({
245+
...tab.route,
246+
onAuxClick: () => void removeTab({ baseRoute, id: tab.id, runtime, tabsRx }),
247+
});
220248

221249
return (
222250
<ListBoxItem

0 commit comments

Comments
 (0)