mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-04-18 14:59:42 +02:00
Fix tab focusability
This commit is contained in:
@@ -11,7 +11,16 @@ import {
|
|||||||
} from '@dnd-kit/core';
|
} from '@dnd-kit/core';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import type { ReactNode, Ref } from 'react';
|
import type { ReactNode, Ref } from 'react';
|
||||||
import { forwardRef, memo, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
|
import {
|
||||||
|
forwardRef,
|
||||||
|
memo,
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useImperativeHandle,
|
||||||
|
useMemo,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
} from 'react';
|
||||||
import { useKeyValue } from '../../../hooks/useKeyValue';
|
import { useKeyValue } from '../../../hooks/useKeyValue';
|
||||||
import { computeSideForDragMove } from '../../../lib/dnd';
|
import { computeSideForDragMove } from '../../../lib/dnd';
|
||||||
import { DropMarker } from '../../DropMarker';
|
import { DropMarker } from '../../DropMarker';
|
||||||
@@ -65,19 +74,22 @@ interface Props {
|
|||||||
activeTabKey?: string;
|
activeTabKey?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Tabs = forwardRef<TabsRef, Props>(function Tabs({
|
export const Tabs = forwardRef<TabsRef, Props>(function Tabs(
|
||||||
defaultValue,
|
{
|
||||||
onChangeValue: onChangeValueProp,
|
defaultValue,
|
||||||
label,
|
onChangeValue: onChangeValueProp,
|
||||||
children,
|
label,
|
||||||
tabs: originalTabs,
|
children,
|
||||||
className,
|
tabs: originalTabs,
|
||||||
tabListClassName,
|
className,
|
||||||
addBorders,
|
tabListClassName,
|
||||||
layout = 'vertical',
|
addBorders,
|
||||||
storageKey,
|
layout = 'vertical',
|
||||||
activeTabKey,
|
storageKey,
|
||||||
}: Props, forwardedRef: Ref<TabsRef>) {
|
activeTabKey,
|
||||||
|
}: Props,
|
||||||
|
forwardedRef: Ref<TabsRef>,
|
||||||
|
) {
|
||||||
const ref = useRef<HTMLDivElement | null>(null);
|
const ref = useRef<HTMLDivElement | null>(null);
|
||||||
const reorderable = !!storageKey;
|
const reorderable = !!storageKey;
|
||||||
|
|
||||||
@@ -92,7 +104,7 @@ export const Tabs = forwardRef<TabsRef, Props>(function Tabs({
|
|||||||
// Migrate old format (string[]) to new format (TabsStorage)
|
// Migrate old format (string[]) to new format (TabsStorage)
|
||||||
const storage: TabsStorage = Array.isArray(rawStorage)
|
const storage: TabsStorage = Array.isArray(rawStorage)
|
||||||
? { order: rawStorage, activeTabs: {} }
|
? { order: rawStorage, activeTabs: {} }
|
||||||
: rawStorage ?? { order: [], activeTabs: {} };
|
: (rawStorage ?? { order: [], activeTabs: {} });
|
||||||
|
|
||||||
const savedOrder = storage.order;
|
const savedOrder = storage.order;
|
||||||
|
|
||||||
@@ -127,11 +139,15 @@ export const Tabs = forwardRef<TabsRef, Props>(function Tabs({
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Expose imperative methods via ref
|
// Expose imperative methods via ref
|
||||||
useImperativeHandle(forwardedRef, () => ({
|
useImperativeHandle(
|
||||||
setActiveTab: (value: string) => {
|
forwardedRef,
|
||||||
onChangeValue(value);
|
() => ({
|
||||||
},
|
setActiveTab: (value: string) => {
|
||||||
}), [onChangeValue]);
|
onChangeValue(value);
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
[onChangeValue],
|
||||||
|
);
|
||||||
|
|
||||||
// Helper to save order
|
// Helper to save order
|
||||||
const setSavedOrder = useCallback(
|
const setSavedOrder = useCallback(
|
||||||
@@ -285,13 +301,10 @@ export const Tabs = forwardRef<TabsRef, Props>(function Tabs({
|
|||||||
items.push(
|
items.push(
|
||||||
<div
|
<div
|
||||||
key={`marker-${t.value}`}
|
key={`marker-${t.value}`}
|
||||||
className={classNames(
|
className={classNames('relative', layout === 'vertical' ? 'w-0' : 'h-0')}
|
||||||
'relative',
|
|
||||||
layout === 'vertical' ? 'w-0' : 'h-0',
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
<DropMarker orientation={layout === 'vertical' ? 'vertical' : 'horizontal'} />
|
<DropMarker orientation={layout === 'vertical' ? 'vertical' : 'horizontal'} />
|
||||||
</div>
|
</div>,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -305,7 +318,7 @@ export const Tabs = forwardRef<TabsRef, Props>(function Tabs({
|
|||||||
reorderable={reorderable}
|
reorderable={reorderable}
|
||||||
isDragging={isDragging?.value === t.value}
|
isDragging={isDragging?.value === t.value}
|
||||||
onChangeValue={onChangeValue}
|
onChangeValue={onChangeValue}
|
||||||
/>
|
/>,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
return items;
|
return items;
|
||||||
@@ -334,12 +347,7 @@ export const Tabs = forwardRef<TabsRef, Props>(function Tabs({
|
|||||||
>
|
>
|
||||||
{tabButtons}
|
{tabButtons}
|
||||||
{hoveredIndex === tabs.length && (
|
{hoveredIndex === tabs.length && (
|
||||||
<div
|
<div className={classNames('relative', layout === 'vertical' ? 'w-0' : 'h-0')}>
|
||||||
className={classNames(
|
|
||||||
'relative',
|
|
||||||
layout === 'vertical' ? 'w-0' : 'h-0',
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<DropMarker orientation={layout === 'vertical' ? 'vertical' : 'horizontal'} />
|
<DropMarker orientation={layout === 'vertical' ? 'vertical' : 'horizontal'} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -420,6 +428,8 @@ function TabButton({
|
|||||||
} = useDraggable({
|
} = useDraggable({
|
||||||
id: tab.value,
|
id: tab.value,
|
||||||
disabled: !reorderable,
|
disabled: !reorderable,
|
||||||
|
// The button inside handles focus
|
||||||
|
attributes: { tabIndex: -1 },
|
||||||
});
|
});
|
||||||
const { setNodeRef: setDroppableRef } = useDroppable({
|
const { setNodeRef: setDroppableRef } = useDroppable({
|
||||||
id: tab.value,
|
id: tab.value,
|
||||||
@@ -501,11 +511,7 @@ function TabButton({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button leftSlot={tab.leftSlot} rightSlot={tab.rightSlot} {...btnProps}>
|
||||||
leftSlot={tab.leftSlot}
|
|
||||||
rightSlot={tab.rightSlot}
|
|
||||||
{...btnProps}
|
|
||||||
>
|
|
||||||
{'label' in tab && tab.label ? tab.label : tab.value}
|
{'label' in tab && tab.label ? tab.label : tab.value}
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user