fix tab index on command palette play buttons

This commit is contained in:
jeffvli
2026-03-18 02:17:31 -07:00
parent 0bdf1dcb75
commit c16eccaecb
3 changed files with 77 additions and 4 deletions
@@ -134,6 +134,7 @@ export const CommandPalette = ({ modalProps }: CommandPaletteProps) => {
const [pages, setPages] = useState<CommandPalettePages[]>([CommandPalettePages.HOME]); const [pages, setPages] = useState<CommandPalettePages[]>([CommandPalettePages.HOME]);
const activePage = pages[pages.length - 1]; const activePage = pages[pages.length - 1];
const isHome = activePage === CommandPalettePages.HOME; const isHome = activePage === CommandPalettePages.HOME;
const commandRootRef = useRef<HTMLDivElement>(null);
const searchInputRef = useRef<HTMLInputElement>(null); const searchInputRef = useRef<HTMLInputElement>(null);
const popPage = useCallback(() => { const popPage = useCallback(() => {
@@ -189,8 +190,33 @@ export const CommandPalette = ({ modalProps }: CommandPaletteProps) => {
if (e.key === 'ArrowDown' || e.key === 'ArrowUp') { if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
searchInputRef.current?.focus(); searchInputRef.current?.focus();
} }
if (e.key === 'Tab' && !e.shiftKey) {
const root = commandRootRef.current;
if (!root) return;
const selectedItem = root.querySelector(
'[cmdk-item][aria-selected="true"]',
) as HTMLElement | null;
if (!selectedItem) return;
const focusTarget = selectedItem.querySelector(
'button:not([disabled]), [tabindex]:not([tabindex="-1"])',
) as HTMLElement | null;
if (!focusTarget) return;
e.preventDefault();
e.stopPropagation();
requestAnimationFrame(() => {
focusTarget.focus();
});
}
}} }}
onValueChange={setValue} onValueChange={setValue}
ref={commandRootRef}
value={value} value={value}
> >
<CommandPaletteSearch <CommandPaletteSearch
@@ -32,3 +32,14 @@
background: alpha(var(--theme-colors-foreground-muted), 0.3); background: alpha(var(--theme-colors-foreground-muted), 0.3);
border-radius: 4px; border-radius: 4px;
} }
.controls {
display: flex;
align-items: center;
justify-content: center;
svg {
width: var(--theme-font-size-sm);
height: var(--theme-font-size-sm);
}
}
@@ -16,6 +16,24 @@ import { Text } from '/@/shared/components/text/text';
import { ExplicitStatus, LibraryItem, Song } from '/@/shared/types/domain-types'; import { ExplicitStatus, LibraryItem, Song } from '/@/shared/types/domain-types';
import { Play } from '/@/shared/types/types'; import { Play } from '/@/shared/types/types';
const createPlayKeyDownHandler = (
playType: Play,
disabled: boolean,
onPlay: (type: Play) => void,
) => {
return (e: React.KeyboardEvent) => {
if (e.key === ' ' || e.key === 'Enter') {
e.preventDefault();
e.stopPropagation();
if (!disabled) {
onPlay(playType);
}
} else if (e.key === 'Tab') {
e.stopPropagation();
}
};
};
interface LibraryCommandItemProps { interface LibraryCommandItemProps {
disabled?: boolean; disabled?: boolean;
explicitStatus?: ExplicitStatus | null; explicitStatus?: ExplicitStatus | null;
@@ -119,29 +137,47 @@ export const LibraryCommandItem = ({
</div> </div>
</div> </div>
{showControls && ( {showControls && (
<ActionIconGroup> <ActionIconGroup className={styles.controls}>
<PlayTooltip disabled={disabled} type={Play.NOW}> <PlayTooltip disabled={disabled} type={Play.NOW}>
<ActionIcon <ActionIcon
icon="mediaPlay" icon="mediaPlay"
variant="subtle" size="xs"
variant="default"
{...handlePlayNow.handlers} {...handlePlayNow.handlers}
{...handlePlayNow.props} {...handlePlayNow.props}
onKeyDown={createPlayKeyDownHandler(
Play.NOW,
Boolean(disabled ?? handlePlayNow.props.disabled),
handlePlay,
)}
/> />
</PlayTooltip> </PlayTooltip>
<PlayTooltip disabled={disabled} type={Play.NEXT}> <PlayTooltip disabled={disabled} type={Play.NEXT}>
<ActionIcon <ActionIcon
icon="mediaPlayNext" icon="mediaPlayNext"
variant="subtle" size="xs"
variant="default"
{...handlePlayNext.handlers} {...handlePlayNext.handlers}
{...handlePlayNext.props} {...handlePlayNext.props}
onKeyDown={createPlayKeyDownHandler(
Play.NEXT,
Boolean(disabled ?? handlePlayNext.props.disabled),
handlePlay,
)}
/> />
</PlayTooltip> </PlayTooltip>
<PlayTooltip disabled={disabled} type={Play.LAST}> <PlayTooltip disabled={disabled} type={Play.LAST}>
<ActionIcon <ActionIcon
icon="mediaPlayLast" icon="mediaPlayLast"
variant="subtle" size="xs"
variant="default"
{...handlePlayLast.handlers} {...handlePlayLast.handlers}
{...handlePlayLast.props} {...handlePlayLast.props}
onKeyDown={createPlayKeyDownHandler(
Play.LAST,
Boolean(disabled ?? handlePlayLast.props.disabled),
handlePlay,
)}
/> />
</PlayTooltip> </PlayTooltip>
</ActionIconGroup> </ActionIconGroup>