* 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) => {
|
(e: globalThis.MouseEvent) => {
|
||||||
if (guard && !guard(e)) return
|
if (guard && !guard(e)) return
|
||||||
e.preventDefault()
|
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 })
|
setPosition({ x: e.clientX, y: e.clientY })
|
||||||
setOpen(true)
|
setOpen(true)
|
||||||
},
|
},
|
||||||
|
@ -273,11 +273,11 @@ export const FileMachineProvider = ({
|
|||||||
: newName + FILE_EXT
|
: newName + FILE_EXT
|
||||||
: DEFAULT_FILE_NAME
|
: DEFAULT_FILE_NAME
|
||||||
const oldPath = window.electron.path.join(
|
const oldPath = window.electron.path.join(
|
||||||
input.selectedDirectory.path,
|
input.parentDirectory.path,
|
||||||
oldName
|
oldName
|
||||||
)
|
)
|
||||||
const newPath = window.electron.path.join(
|
const newPath = window.electron.path.join(
|
||||||
input.selectedDirectory.path,
|
input.parentDirectory.path,
|
||||||
name
|
name
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -69,37 +69,57 @@ function TreeEntryInput(props: {
|
|||||||
|
|
||||||
function RenameForm({
|
function RenameForm({
|
||||||
fileOrDir,
|
fileOrDir,
|
||||||
|
parentDir,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
level = 0,
|
level = 0,
|
||||||
}: {
|
}: {
|
||||||
fileOrDir: FileEntry
|
fileOrDir: FileEntry
|
||||||
|
parentDir: FileEntry | undefined
|
||||||
onSubmit: () => void
|
onSubmit: () => void
|
||||||
level?: number
|
level?: number
|
||||||
}) {
|
}) {
|
||||||
const { send } = useFileContext()
|
const { send } = useFileContext()
|
||||||
const inputRef = useRef<HTMLInputElement>(null)
|
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({
|
send({
|
||||||
type: 'Rename file',
|
type: 'Rename file',
|
||||||
data: {
|
data: {
|
||||||
oldName: fileOrDir.name || '',
|
oldName: fileOrDir.name || '',
|
||||||
newName: inputRef.current?.value || fileOrDir.name || '',
|
newName: inputRef.current?.value || fileOrDir.name || '',
|
||||||
isDir: fileOrDir.children !== null,
|
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>) {
|
function handleKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {
|
||||||
if (e.key === 'Escape') {
|
if (e.key === 'Escape') {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
onSubmit()
|
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 (
|
return (
|
||||||
<form onSubmit={handleRenameSubmit}>
|
<form onKeyUp={handleRenameSubmit}>
|
||||||
<label>
|
<label>
|
||||||
<span className="sr-only">Rename file</span>
|
<span className="sr-only">Rename file</span>
|
||||||
<input
|
<input
|
||||||
@ -156,7 +176,6 @@ const FileTreeItem = ({
|
|||||||
parentDir,
|
parentDir,
|
||||||
project,
|
project,
|
||||||
currentFile,
|
currentFile,
|
||||||
lastDirectoryClicked,
|
|
||||||
fileOrDir,
|
fileOrDir,
|
||||||
onNavigateToFile,
|
onNavigateToFile,
|
||||||
onClickDirectory,
|
onClickDirectory,
|
||||||
@ -173,7 +192,6 @@ const FileTreeItem = ({
|
|||||||
parentDir: FileEntry | undefined
|
parentDir: FileEntry | undefined
|
||||||
project?: IndexLoaderData['project']
|
project?: IndexLoaderData['project']
|
||||||
currentFile?: IndexLoaderData['file']
|
currentFile?: IndexLoaderData['file']
|
||||||
lastDirectoryClicked?: FileEntry
|
|
||||||
fileOrDir: FileEntry
|
fileOrDir: FileEntry
|
||||||
onNavigateToFile?: () => void
|
onNavigateToFile?: () => void
|
||||||
onClickDirectory: (
|
onClickDirectory: (
|
||||||
@ -355,6 +373,7 @@ const FileTreeItem = ({
|
|||||||
) : (
|
) : (
|
||||||
<RenameForm
|
<RenameForm
|
||||||
fileOrDir={fileOrDir}
|
fileOrDir={fileOrDir}
|
||||||
|
parentDir={parentDir}
|
||||||
onSubmit={removeCurrentItemFromRenaming}
|
onSubmit={removeCurrentItemFromRenaming}
|
||||||
level={level}
|
level={level}
|
||||||
/>
|
/>
|
||||||
@ -401,6 +420,7 @@ const FileTreeItem = ({
|
|||||||
/>
|
/>
|
||||||
<RenameForm
|
<RenameForm
|
||||||
fileOrDir={fileOrDir}
|
fileOrDir={fileOrDir}
|
||||||
|
parentDir={parentDir}
|
||||||
onSubmit={removeCurrentItemFromRenaming}
|
onSubmit={removeCurrentItemFromRenaming}
|
||||||
level={-1}
|
level={-1}
|
||||||
/>
|
/>
|
||||||
@ -454,7 +474,6 @@ const FileTreeItem = ({
|
|||||||
onCloneFileOrFolder={onCloneFileOrFolder}
|
onCloneFileOrFolder={onCloneFileOrFolder}
|
||||||
onOpenInNewWindow={onOpenInNewWindow}
|
onOpenInNewWindow={onOpenInNewWindow}
|
||||||
newTreeEntry={newTreeEntry}
|
newTreeEntry={newTreeEntry}
|
||||||
lastDirectoryClicked={lastDirectoryClicked}
|
|
||||||
onClickDirectory={onClickDirectory}
|
onClickDirectory={onClickDirectory}
|
||||||
onNavigateToFile={onNavigateToFile}
|
onNavigateToFile={onNavigateToFile}
|
||||||
level={level + 1}
|
level={level + 1}
|
||||||
@ -690,10 +709,6 @@ export const FileTreeInner = ({
|
|||||||
const { errors } = useKclContext()
|
const { errors } = useKclContext()
|
||||||
const runtimeErrors = kclErrorsByFilename(errors)
|
const runtimeErrors = kclErrorsByFilename(errors)
|
||||||
|
|
||||||
const [lastDirectoryClicked, setLastDirectoryClicked] = useState<
|
|
||||||
FileEntry | undefined
|
|
||||||
>(undefined)
|
|
||||||
|
|
||||||
const [treeSelection, setTreeSelection] = useState<FileEntry | undefined>(
|
const [treeSelection, setTreeSelection] = useState<FileEntry | undefined>(
|
||||||
loaderData.file
|
loaderData.file
|
||||||
)
|
)
|
||||||
@ -751,7 +766,6 @@ export const FileTreeInner = ({
|
|||||||
if (!target) return
|
if (!target) return
|
||||||
|
|
||||||
setTreeSelection(target)
|
setTreeSelection(target)
|
||||||
setLastDirectoryClicked(target)
|
|
||||||
fileSend({
|
fileSend({
|
||||||
type: 'Set selected directory',
|
type: 'Set selected directory',
|
||||||
directory: target,
|
directory: target,
|
||||||
@ -787,7 +801,6 @@ export const FileTreeInner = ({
|
|||||||
parentDir={fileContext.project}
|
parentDir={fileContext.project}
|
||||||
project={fileContext.project}
|
project={fileContext.project}
|
||||||
currentFile={loaderData?.file}
|
currentFile={loaderData?.file}
|
||||||
lastDirectoryClicked={lastDirectoryClicked}
|
|
||||||
fileOrDir={fileOrDir}
|
fileOrDir={fileOrDir}
|
||||||
onCreateFile={onCreateFile}
|
onCreateFile={onCreateFile}
|
||||||
onCreateFolder={onCreateFolder}
|
onCreateFolder={onCreateFolder}
|
||||||
|
@ -3,7 +3,7 @@ import { assign, fromPromise, setup } from 'xstate'
|
|||||||
import type { FileEntry, Project } from '@src/lib/project'
|
import type { FileEntry, Project } from '@src/lib/project'
|
||||||
|
|
||||||
type FileMachineContext = {
|
type FileMachineContext = {
|
||||||
project: Project
|
project: Project // this is also the root directory
|
||||||
selectedDirectory: FileEntry
|
selectedDirectory: FileEntry
|
||||||
itemsBeingRenamed: (FileEntry | string)[]
|
itemsBeingRenamed: (FileEntry | string)[]
|
||||||
}
|
}
|
||||||
@ -13,7 +13,12 @@ type FileMachineEvents =
|
|||||||
| { type: 'Open file in new window'; data: { name: string } }
|
| { type: 'Open file in new window'; data: { name: string } }
|
||||||
| {
|
| {
|
||||||
type: 'Rename file'
|
type: 'Rename file'
|
||||||
data: { oldName: string; newName: string; isDir: boolean }
|
data: {
|
||||||
|
oldName: string
|
||||||
|
newName: string
|
||||||
|
isDir: boolean
|
||||||
|
parentDirectory: FileEntry
|
||||||
|
}
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: 'Create file'
|
type: 'Create file'
|
||||||
@ -145,7 +150,7 @@ export const fileMachine = setup({
|
|||||||
oldName: string
|
oldName: string
|
||||||
newName: string
|
newName: string
|
||||||
isDir: boolean
|
isDir: boolean
|
||||||
selectedDirectory: FileEntry
|
parentDirectory: FileEntry
|
||||||
}
|
}
|
||||||
}) => Promise.resolve({ message: '', newPath: '', oldPath: '' })
|
}) => Promise.resolve({ message: '', newPath: '', oldPath: '' })
|
||||||
),
|
),
|
||||||
@ -324,14 +329,14 @@ export const fileMachine = setup({
|
|||||||
oldName: '',
|
oldName: '',
|
||||||
newName: '',
|
newName: '',
|
||||||
isDir: false,
|
isDir: false,
|
||||||
selectedDirectory: {} as FileEntry,
|
parentDirectory: {} as FileEntry,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
oldName: event.data.oldName,
|
oldName: event.data.oldName,
|
||||||
newName: event.data.newName,
|
newName: event.data.newName,
|
||||||
isDir: event.data.isDir,
|
isDir: event.data.isDir,
|
||||||
selectedDirectory: context.selectedDirectory,
|
parentDirectory: event.data.parentDirectory,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user