Skip to content

Commit

Permalink
Fix focus states in file tree (#393)
Browse files Browse the repository at this point in the history
  • Loading branch information
benjreinhart authored Oct 20, 2024
1 parent 32d0901 commit e9646df
Showing 1 changed file with 23 additions and 29 deletions.
52 changes: 23 additions & 29 deletions packages/web/src/components/apps/panels/explorer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,17 @@ function FolderNode(props: {
}
/>
</ContextMenuTrigger>
<ContextMenuContent>
<ContextMenuContent
onCloseAutoFocus={(e) => {
// This is an important line of code. It is needed to prevent focus
// from returning to other elements when this menu is closed. Without this,
// when a user clicks "New [file|folder]" or "Rename", the input box will
// render and sometimes immediately dismiss because this returns focus to
// the button element the user right clicked on, causing the input's onBlur
// to trigger.
e.preventDefault();
}}
>
<ContextMenuItem onClick={props.onNewFile}>New file...</ContextMenuItem>
<ContextMenuItem onClick={props.onNewfolder}>New folder...</ContextMenuItem>
<ContextMenuItem onClick={props.onRename}>Rename</ContextMenuItem>
Expand All @@ -300,38 +310,22 @@ function EditNameNode(props: {
const ref = useRef<HTMLInputElement>(null);

useEffect(() => {
const input = ref.current;
if (!input) return;

const focusAndSelect = () => {
input.focus();
const idx = input.value.lastIndexOf('.');
input.setSelectionRange(0, idx === -1 ? input.value.length : idx);
};
function focusAndSelect() {
const input = ref.current;

focusAndSelect();

// Re-focus if the input loses focus
const handleFocusOut = () => {
if (document.activeElement !== input) {
focusAndSelect();
if (input) {
input.focus();
const idx = input.value.lastIndexOf('.');
input.setSelectionRange(0, idx === -1 ? input.value.length : idx);
}
};

document.addEventListener('focusin', handleFocusOut);
}

return () => {
document.removeEventListener('focusin', handleFocusOut);
};
// This setTimeout is intentional. We need to draw focus to this
// input after the current event loop clears out because other elements
// are getting focused in some situations immediately after this renders.
setTimeout(focusAndSelect, 0);
}, []);

function onBlur(e: React.FocusEvent<HTMLInputElement>) {
// Only cancel if the new active element is outside this component
if (!e.currentTarget.contains(e.relatedTarget as Node)) {
props.onCancel();
}
}

return (
<input
ref={ref}
Expand All @@ -342,7 +336,7 @@ function EditNameNode(props: {
'[&::selection]:bg-accent [&::selection]:text-accent-foreground',
)}
style={{ paddingLeft: `${props.depth * 12}px` }}
onBlur={onBlur}
onBlur={props.onCancel}
onKeyDown={(e) => {
if (e.key === 'Enter' && ref.current) {
e.preventDefault();
Expand Down

0 comments on commit e9646df

Please sign in to comment.