Skip to content

Commit 5523054

Browse files
committed
🐛 fix(base-ui): prevent switch item stealing highlight in dropdown/context menu
antd Switch button grabs native focus on click; the focus event bubbles to Menu.Item where Floating UI's useListNavigation syncs activeIndex, leaving the item highlighted after reopen. Wrap Switch in a focus-stopping span and mark it tabIndex=-1 so it no longer drives menu activeIndex.
1 parent d0382b8 commit 5523054

5 files changed

Lines changed: 77 additions & 16 deletions

File tree

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { ActionIcon, DropdownMenu } from '@lobehub/ui';
2+
import {
3+
CopyIcon,
4+
HashIcon,
5+
Maximize2Icon,
6+
MoreHorizontalIcon,
7+
PencilIcon,
8+
SparklesIcon,
9+
StarIcon,
10+
TrashIcon,
11+
} from 'lucide-react';
12+
import { useMemo, useState } from 'react';
13+
14+
import type { DropdownItem } from '../type';
15+
16+
export default () => {
17+
const [fullWidth, setFullWidth] = useState(false);
18+
19+
const items = useMemo<DropdownItem[]>(
20+
() => [
21+
{ icon: <StarIcon />, key: 'favorite', label: '收藏' },
22+
{ icon: <SparklesIcon />, key: 'smart-rename', label: '智能重命名' },
23+
{ icon: <PencilIcon />, key: 'rename', label: '重命名' },
24+
{ type: 'divider' },
25+
{ icon: <HashIcon />, key: 'copy-id', label: '复制会话 ID' },
26+
{ icon: <CopyIcon />, key: 'copy', label: '复制' },
27+
{ type: 'divider' },
28+
{
29+
checked: fullWidth,
30+
icon: <Maximize2Icon />,
31+
key: 'full-width',
32+
label: '全宽显示',
33+
onCheckedChange: setFullWidth,
34+
type: 'switch',
35+
},
36+
{ type: 'divider' },
37+
{ danger: true, icon: <TrashIcon />, key: 'delete', label: '删除' },
38+
],
39+
[fullWidth],
40+
);
41+
42+
return (
43+
<DropdownMenu items={items}>
44+
<ActionIcon icon={MoreHorizontalIcon} />
45+
</DropdownMenu>
46+
);
47+
};

src/DropdownMenu/index.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ When items have descriptions, use `iconAlign` to control icon vertical alignment
5757

5858
<code src="./demos/switch.tsx" center></code>
5959

60+
<code src="./demos/switch-mixed.tsx" center></code>
61+
6062
## Lazy Items
6163

6264
<code src="./demos/lazy.tsx" center></code>

src/base-ui/ContextMenu/renderItems.tsx

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -99,14 +99,19 @@ const ContextMenuSwitchItemInternal = ({
9999
}}
100100
>
101101
{children}
102-
<Switch
103-
checked={checked}
104-
disabled={disabled}
105-
size="small"
106-
style={{ marginInlineStart: 16 }}
107-
onChange={handleCheckedChange}
108-
onClick={(_, e) => e.stopPropagation()}
109-
/>
102+
<span
103+
style={{ display: 'inline-flex', marginInlineStart: 16 }}
104+
onFocus={(e) => e.stopPropagation()}
105+
>
106+
<Switch
107+
checked={checked}
108+
disabled={disabled}
109+
size="small"
110+
tabIndex={-1}
111+
onChange={handleCheckedChange}
112+
onClick={(_, e) => e.stopPropagation()}
113+
/>
114+
</span>
110115
</ContextMenu.Item>
111116
);
112117
};

src/base-ui/DropdownMenu/atoms.tsx

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -361,14 +361,19 @@ export const DropdownMenuSwitchItem = ({
361361
}}
362362
>
363363
{children}
364-
<Switch
365-
checked={checked}
366-
disabled={disabled}
367-
size="small"
368-
style={{ marginInlineStart: 16 }}
369-
onChange={handleCheckedChange}
370-
onClick={(_, e) => e.stopPropagation()}
371-
/>
364+
<span
365+
style={{ display: 'inline-flex', marginInlineStart: 16 }}
366+
onFocus={(e) => e.stopPropagation()}
367+
>
368+
<Switch
369+
checked={checked}
370+
disabled={disabled}
371+
size="small"
372+
tabIndex={-1}
373+
onChange={handleCheckedChange}
374+
onClick={(_, e) => e.stopPropagation()}
375+
/>
376+
</span>
372377
</Menu.Item>
373378
);
374379
};

src/base-ui/DropdownMenu/index.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ When items have descriptions, use `iconAlign` to control icon vertical alignment
6262

6363
<code src="../../DropdownMenu/demos/switch.tsx" center></code>
6464

65+
<code src="../../DropdownMenu/demos/switch-mixed.tsx" center></code>
66+
6567
## Lazy Items
6668

6769
<code src="../../DropdownMenu/demos/lazy.tsx" center></code>

0 commit comments

Comments
 (0)