Closed aghster closed 6 months ago
Hey @aghster, I'd need to see a code sample to see what the problem is. By default when using Command.Dialog
, the input is unmounted and so the value is cleared. If you're passing value
and onValueChange
to the input, then the input state will be preserved – and if the input is the first focusable element within the dialog, it will be focused and have the text contents selected already, which is your desired behavior.
Unfortunately I am not very familiar with React. It tried accessing the input field within a function called by onOpenChange. However, as it seems, when that function is called the input field is not yet part of the DOM and therefore cannot be accessed.
You can try useEffect
to react to state changes here. It runs after the DOM is synced, so you may have more success:
const [open, setOpen] = React.useState(false);
const [search, setSearch] = React.useState('');
const inputRef = React.useRef(null);
React.useEffect(() => {
if (open && inputRef.current) {
inputRef.current.select()
}
}, [open])
<Command.Input value={search} onValueChange={setSearch} />
Hey @pacocoursey, thanks for your help. Unfortunately, I did not manage to work it out. This is what I tried:
import React from 'react';
import { Command } from 'cmdk';
export default function CommandMenu() {
const [open, setOpen] = React.useState(false);
const [search, setSearch] = React.useState('');
const inputRef = React.useRef(null)
React.useEffect(() => {
const down = (e) => {
if (e.key === ' ' && (e.metaKey || e.ctrlKey)) {
setOpen((open) => !open)
}
}
document.addEventListener('keydown', down)
return () => document.removeEventListener('keydown', down)
}, [])
React.useEffect(() => {
if (open && inputRef.current) {
inputRef.current.select();
}
}, [open]);
return (
<Command.Dialog open={open} onOpenChange={setOpen} label="Global Command Menu">
<Command.Input ref={inputRef} autoFocus value={search} onValueChange={setSearch} placeholder="Search..." />
<Command.List>
<Command.Empty>No results found.</Command.Empty>
<Command.Group heading="Letters">
<Command.Item>a</Command.Item>
<Command.Item>b</Command.Item>
<Command.Separator />
<Command.Item>c</Command.Item>
</Command.Group>
<Command.Item>Apple</Command.Item>
</Command.List>
</Command.Dialog>
)
};
To my understanding, the additional useEffect
is called as soon as open
gets true
, but at that time <Command.Input />
is not yet added to the DOM so that inputRef.current
is still null
and inputRef.current.select()
is never called. Debugging in Firefox seems to confirm this:
By the way, when the dialog gets closed, it's just the other way round: inputRef.current
points to the <Command.Input />
, but open
is false.
Is there anything else I can try?
You might want to try selecting the input contents after the next tick in the event loop:
React.useEffect(() => {
if (open && inputRef.current) {
setTimeout(() => {
inputRef.current.select();
})
}
}, [open]);
In my use case, ⌘K is shown in dialog mode. If I open ⌘K, enter a search string into the input field, then close ⌘K and then re-open ⌘K again, the search string entered before is still there. In general, this is quite useful, as I can easily repeat or amend my previous search.
Usually, however, I rather want to enter a new search. Currently, after ⌘K is re-opened, the input field gets focus with the cursor placed at the end of the previously entered search string. Hence, replacing that search string with a new search string requires a few keystrokes. In my opinion, it would be more convenient if the previously entered search string was automatically selected when ⌘K is re-opened. This way, if I want to enter a new search string, I can just type right away, but I am also still able to use the old search string, if I want to.
Therefore, I am looking for a way to configure ⌘K such that when ⌘K opens, the content of the search input field gets automatically selected (if the input field has no content, of course nothing gets selected). Unfortunately I am not very familiar with React. It tried accessing the input field within a function called by
onOpenChange
. However, as it seems, when that function is called the input field is not yet part of the DOM and therefore cannot be accessed. Furthermore, I found that Radix UI's Dialog component has anonOpenAutoFocus
event handler. Can it be used with ⌘K? Or is there any other simple way to automatically select the contents of the input field when ⌘K opens?