Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { App, Form, GetProps, Input, theme, Typography } from 'antd';
import { createStyles } from 'antd-style';
import _ from 'lodash';
import { CornerDownLeftIcon } from 'lucide-react';
import { use, useState } from 'react';
import { use, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';

interface ServerError extends Error {
Expand Down Expand Up @@ -62,6 +62,7 @@ const EditableFileName: React.FC<EditableNameProps> = ({
style,
...props
}) => {
'use memo';
const { t } = useTranslation();
const { token } = theme.useToken();
const { modal, message } = App.useApp();
Expand Down Expand Up @@ -104,10 +105,20 @@ const EditableFileName: React.FC<EditableNameProps> = ({
const isPendingRenamingAndRefreshing =
renameMutation.isPending || optimisticName !== fileInfo.name;

// focus back to the text component after editing for better UX related to keyboard shortcuts
const textRef = useRef<HTMLElement>(null);
const focusFallback = () => {
setTimeout(() => {
textRef.current?.focus();
}, 0);
};

return (
<>
{!isEditing || isPendingRenamingAndRefreshing ? (
<Component
ref={textRef}
tabIndex={-1}
editable={
!disabled && !isPendingRenamingAndRefreshing
? {
Expand All @@ -123,7 +134,11 @@ const EditableFileName: React.FC<EditableNameProps> = ({
: false
}
className={!disabled ? styles.hoverEdit : undefined}
style={style}
style={{
// after editing, focus this element, remove outline
outline: 'none',
...style,
}}
{...props}
>
{fileInfo?.type === 'DIRECTORY' ? (
Expand Down Expand Up @@ -167,6 +182,7 @@ const EditableFileName: React.FC<EditableNameProps> = ({
initialValues={{ newName: fileInfo?.name }}
onFinish={(values) => {
setIsEditing(false);
focusFallback();
setOptimisticName(values.newName);
const variables = {
target_path: _.join([currentPath, fileInfo?.name], '/'),
Expand Down Expand Up @@ -242,7 +258,9 @@ const EditableFileName: React.FC<EditableNameProps> = ({
autoFocus
onKeyDown={(e) => {
if (e.key === 'Escape') {
e.stopPropagation();
setIsEditing(false);
focusFallback();
}
}}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { useCurrentProjectValue } from '../../hooks/useCurrentProject';
import { useValidateSessionName } from '../../hooks/useValidateSessionName';
import { theme, Form, Input, App, GetProps, Typography } from 'antd';
import { CornerDownLeftIcon } from 'lucide-react';
import React, { useState } from 'react';
import React, { useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import {
graphql,
Expand Down Expand Up @@ -36,6 +36,7 @@ const EditableSessionName: React.FC<EditableSessionNameProps> = ({
style,
...otherProps
}) => {
'use memo';
const relayEvn = useRelayEnvironment();
const currentProject = useCurrentProjectValue();
const session = useFragment(
Expand Down Expand Up @@ -89,10 +90,21 @@ const EditableSessionName: React.FC<EditableSessionNameProps> = ({

const isPendingRenamingAndRefreshing =
renameSessionMutation.isPending || optimisticName !== session.name;

// focus back to the text component after editing for better UX related to keyboard shortcuts
const textRef = useRef<HTMLElement>(null);
const focusFallback = () => {
setTimeout(() => {
textRef.current?.focus();
}, 0);
};

return (
<>
{(!isEditing || isPendingRenamingAndRefreshing) && (
<Component
ref={textRef}
tabIndex={-1}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we modify the tabIndex?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For text nodes such as <span>, the .focus() method in JavaScript doesn't work unless the element has a tabindex attribute.
Without this attribute, after exiting the editing mode by pressing Esc once, pressing Esc again will not close the drawer, because in that case, the activeElement becomes the body.
We set tabindex="-1" so that the element can only be focused programmatically, not through keyboard navigation.

editable={
isEditingAllowed && !isPendingRenamingAndRefreshing
? {
Expand All @@ -105,6 +117,8 @@ const EditableSessionName: React.FC<EditableSessionNameProps> = ({
}
copyable
style={{
// after editing, focus this element, remove outline
outline: 'none',
...style,
color: isPendingRenamingAndRefreshing
? token.colorTextTertiary
Expand All @@ -121,6 +135,7 @@ const EditableSessionName: React.FC<EditableSessionNameProps> = ({
<Form
onFinish={(values) => {
setIsEditing(false);
focusFallback();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should focusFallback also be included in onFinish? When editing becomes false, the form focus is automatically released.

setOptimisticName(values.sessionName);
// FIXME: This API does not return any response on success or error.
renameSessionMutation.mutate(values.sessionName, {
Expand Down Expand Up @@ -190,7 +205,9 @@ const EditableSessionName: React.FC<EditableSessionNameProps> = ({
onKeyDown={(e) => {
// when press escape key, cancel editing
if (e.key === 'Escape') {
e.stopPropagation();
setIsEditing(false);
focusFallback();
}
}}
/>
Expand Down
18 changes: 17 additions & 1 deletion react/src/components/EditableVFolderName.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
import { BAILink, toLocalId, useErrorMessageResolver } from 'backend.ai-ui';
import _ from 'lodash';
import { CornerDownLeftIcon } from 'lucide-react';
import React, { useState } from 'react';
import React, { useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import {
graphql,
Expand Down Expand Up @@ -55,6 +55,7 @@ const EditableVFolderName: React.FC<EditableVFolderNameProps> = ({
inputProps,
...otherProps
}) => {
'use memo';
const vfolder = useFragment(
graphql`
fragment EditableVFolderNameFragment on VirtualFolderNode {
Expand Down Expand Up @@ -93,10 +94,20 @@ const EditableVFolderName: React.FC<EditableVFolderNameProps> = ({
const isPendingRenameMutation =
renameMutation.isPending || optimisticName !== vfolder.name;

// focus back to the text component after editing for better UX related to keyboard shortcuts
const textRef = useRef<HTMLElement>(null);
const focusFallback = () => {
setTimeout(() => {
textRef.current?.focus();
}, 0);
};

return (
<>
{(!isEditing || isPendingRenameMutation) && (
<Component
ref={textRef}
tabIndex={-1}
editable={
isEditingAllowed && !isPendingRenameMutation
? {
Expand All @@ -118,6 +129,8 @@ const EditableVFolderName: React.FC<EditableVFolderNameProps> = ({
: false
}
style={{
// after editing, focus this element, remove outline
outline: 'none',
...style,
color: isPendingRenameMutation
? token.colorTextTertiary
Expand All @@ -142,6 +155,7 @@ const EditableVFolderName: React.FC<EditableVFolderNameProps> = ({
<Form
onFinish={(values) => {
setIsEditing(false);
focusFallback();
if (values.vfolderName === vfolder.name) {
return;
}
Expand Down Expand Up @@ -238,7 +252,9 @@ const EditableVFolderName: React.FC<EditableVFolderNameProps> = ({
onKeyDown={(e) => {
// when press escape key, cancel editing
if (e.key === 'Escape') {
e.stopPropagation();
setIsEditing(false);
focusFallback();
onEditEnd?.();
}
}}
Expand Down
1 change: 1 addition & 0 deletions react/src/components/FolderExplorerModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ const FolderExplorerModal: React.FC<FolderExplorerProps> = ({
className={styles.baiModalHeader}
width={'90%'}
centered
keyboard
destroyOnHidden
footer={null}
title={
Expand Down