* fix going into rename mode for files with parents * lastDirectoryClicked is not used * fix file/folder rename bugs: renaming within folders * Turn form into div to fix issue of child renaming continues into renaming the parent folder when hitting enter * ContextMenu stopPropagation not needed anymore, maybe because of form refactor * ContextMenu IS needed actually, with multiple nested folders * make lint happy * Update src/components/ContextMenu.tsx Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com> * re-add <form> instead of <div> for file renaming --------- Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com> Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>
This commit is contained in:
@ -43,6 +43,10 @@ export function ContextMenu({
|
||||
(e: globalThis.MouseEvent) => {
|
||||
if (guard && !guard(e)) return
|
||||
e.preventDefault()
|
||||
// This stopPropagation is needed in case multiple nested items use a separate context menu each,
|
||||
// which would cause the parent context menu to receive the event even if the child was clicked on.
|
||||
// Eg. this happens in FileTree causing a bug where the parent folder is going into renaming mode when trying to rename a child.
|
||||
e.stopPropagation()
|
||||
setPosition({ x: e.clientX, y: e.clientY })
|
||||
setOpen(true)
|
||||
},
|
||||
|
@ -273,11 +273,11 @@ export const FileMachineProvider = ({
|
||||
: newName + FILE_EXT
|
||||
: DEFAULT_FILE_NAME
|
||||
const oldPath = window.electron.path.join(
|
||||
input.selectedDirectory.path,
|
||||
input.parentDirectory.path,
|
||||
oldName
|
||||
)
|
||||
const newPath = window.electron.path.join(
|
||||
input.selectedDirectory.path,
|
||||
input.parentDirectory.path,
|
||||
name
|
||||
)
|
||||
|
||||
|
@ -69,37 +69,57 @@ function TreeEntryInput(props: {
|
||||
|
||||
function RenameForm({
|
||||
fileOrDir,
|
||||
parentDir,
|
||||
onSubmit,
|
||||
level = 0,
|
||||
}: {
|
||||
fileOrDir: FileEntry
|
||||
parentDir: FileEntry | undefined
|
||||
onSubmit: () => void
|
||||
level?: number
|
||||
}) {
|
||||
const { send } = useFileContext()
|
||||
const inputRef = useRef<HTMLInputElement>(null)
|
||||
const fileContext = useFileContext()
|
||||
|
||||
function handleRenameSubmit(e: React.KeyboardEvent<HTMLElement>) {
|
||||
if (e.key !== 'Enter') {
|
||||
return
|
||||
}
|
||||
|
||||
function handleRenameSubmit(e: React.FormEvent<HTMLFormElement>) {
|
||||
e.preventDefault()
|
||||
send({
|
||||
type: 'Rename file',
|
||||
data: {
|
||||
oldName: fileOrDir.name || '',
|
||||
newName: inputRef.current?.value || fileOrDir.name || '',
|
||||
isDir: fileOrDir.children !== null,
|
||||
parentDirectory: parentDir ?? fileContext.context.project,
|
||||
},
|
||||
})
|
||||
|
||||
// To get out of the renaming state, without this the current file is still in renaming mode
|
||||
onSubmit()
|
||||
}
|
||||
|
||||
function handleKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {
|
||||
if (e.key === 'Escape') {
|
||||
e.stopPropagation()
|
||||
onSubmit()
|
||||
} else if (e.key === 'Enter') {
|
||||
// This is needed to prevent events to bubble up and the form to be submitted.
|
||||
// (Alternatively the form could be changed into a div.)
|
||||
// Bug without this:
|
||||
// - open a parent folder (close and open if it's already open)
|
||||
// - right click -> rename one of its children
|
||||
// - give new name and press enter
|
||||
// -> new name is not applied, old name is reverted
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<form onSubmit={handleRenameSubmit}>
|
||||
<form onKeyUp={handleRenameSubmit}>
|
||||
<label>
|
||||
<span className="sr-only">Rename file</span>
|
||||
<input
|
||||
@ -156,7 +176,6 @@ const FileTreeItem = ({
|
||||
parentDir,
|
||||
project,
|
||||
currentFile,
|
||||
lastDirectoryClicked,
|
||||
fileOrDir,
|
||||
onNavigateToFile,
|
||||
onClickDirectory,
|
||||
@ -173,7 +192,6 @@ const FileTreeItem = ({
|
||||
parentDir: FileEntry | undefined
|
||||
project?: IndexLoaderData['project']
|
||||
currentFile?: IndexLoaderData['file']
|
||||
lastDirectoryClicked?: FileEntry
|
||||
fileOrDir: FileEntry
|
||||
onNavigateToFile?: () => void
|
||||
onClickDirectory: (
|
||||
@ -355,6 +373,7 @@ const FileTreeItem = ({
|
||||
) : (
|
||||
<RenameForm
|
||||
fileOrDir={fileOrDir}
|
||||
parentDir={parentDir}
|
||||
onSubmit={removeCurrentItemFromRenaming}
|
||||
level={level}
|
||||
/>
|
||||
@ -401,6 +420,7 @@ const FileTreeItem = ({
|
||||
/>
|
||||
<RenameForm
|
||||
fileOrDir={fileOrDir}
|
||||
parentDir={parentDir}
|
||||
onSubmit={removeCurrentItemFromRenaming}
|
||||
level={-1}
|
||||
/>
|
||||
@ -454,7 +474,6 @@ const FileTreeItem = ({
|
||||
onCloneFileOrFolder={onCloneFileOrFolder}
|
||||
onOpenInNewWindow={onOpenInNewWindow}
|
||||
newTreeEntry={newTreeEntry}
|
||||
lastDirectoryClicked={lastDirectoryClicked}
|
||||
onClickDirectory={onClickDirectory}
|
||||
onNavigateToFile={onNavigateToFile}
|
||||
level={level + 1}
|
||||
@ -690,10 +709,6 @@ export const FileTreeInner = ({
|
||||
const { errors } = useKclContext()
|
||||
const runtimeErrors = kclErrorsByFilename(errors)
|
||||
|
||||
const [lastDirectoryClicked, setLastDirectoryClicked] = useState<
|
||||
FileEntry | undefined
|
||||
>(undefined)
|
||||
|
||||
const [treeSelection, setTreeSelection] = useState<FileEntry | undefined>(
|
||||
loaderData.file
|
||||
)
|
||||
@ -751,7 +766,6 @@ export const FileTreeInner = ({
|
||||
if (!target) return
|
||||
|
||||
setTreeSelection(target)
|
||||
setLastDirectoryClicked(target)
|
||||
fileSend({
|
||||
type: 'Set selected directory',
|
||||
directory: target,
|
||||
@ -787,7 +801,6 @@ export const FileTreeInner = ({
|
||||
parentDir={fileContext.project}
|
||||
project={fileContext.project}
|
||||
currentFile={loaderData?.file}
|
||||
lastDirectoryClicked={lastDirectoryClicked}
|
||||
fileOrDir={fileOrDir}
|
||||
onCreateFile={onCreateFile}
|
||||
onCreateFolder={onCreateFolder}
|
||||
|
@ -3,7 +3,7 @@ import { assign, fromPromise, setup } from 'xstate'
|
||||
import type { FileEntry, Project } from '@src/lib/project'
|
||||
|
||||
type FileMachineContext = {
|
||||
project: Project
|
||||
project: Project // this is also the root directory
|
||||
selectedDirectory: FileEntry
|
||||
itemsBeingRenamed: (FileEntry | string)[]
|
||||
}
|
||||
@ -13,7 +13,12 @@ type FileMachineEvents =
|
||||
| { type: 'Open file in new window'; data: { name: string } }
|
||||
| {
|
||||
type: 'Rename file'
|
||||
data: { oldName: string; newName: string; isDir: boolean }
|
||||
data: {
|
||||
oldName: string
|
||||
newName: string
|
||||
isDir: boolean
|
||||
parentDirectory: FileEntry
|
||||
}
|
||||
}
|
||||
| {
|
||||
type: 'Create file'
|
||||
@ -145,7 +150,7 @@ export const fileMachine = setup({
|
||||
oldName: string
|
||||
newName: string
|
||||
isDir: boolean
|
||||
selectedDirectory: FileEntry
|
||||
parentDirectory: FileEntry
|
||||
}
|
||||
}) => Promise.resolve({ message: '', newPath: '', oldPath: '' })
|
||||
),
|
||||
@ -324,14 +329,14 @@ export const fileMachine = setup({
|
||||
oldName: '',
|
||||
newName: '',
|
||||
isDir: false,
|
||||
selectedDirectory: {} as FileEntry,
|
||||
parentDirectory: {} as FileEntry,
|
||||
}
|
||||
}
|
||||
return {
|
||||
oldName: event.data.oldName,
|
||||
newName: event.data.newName,
|
||||
isDir: event.data.isDir,
|
||||
selectedDirectory: context.selectedDirectory,
|
||||
parentDirectory: event.data.parentDirectory,
|
||||
}
|
||||
},
|
||||
|
||||
|
Reference in New Issue
Block a user