fix: moved path into the file entry

This commit is contained in:
Kevin
2025-06-17 09:09:19 -05:00
parent 199fffc442
commit 14ef435b1b
3 changed files with 130 additions and 78 deletions

View File

@ -5,7 +5,6 @@ import {
type FileExplorerRow,
type FileExplorerRender,
type FileExplorerRowContextMenuProps,
constructPath,
} from '@src/components/Explorer/utils'
import { ContextMenu, ContextMenuItem } from '@src/components/ContextMenu'
import { useRef } from 'react'
@ -59,19 +58,18 @@ const Spacer = (level: number) => {
export const FileExplorer = ({
rowsToRender,
selectedRow,
renamingRow,
}: {
rowsToRender: FileExplorerRow[]
selectedRow: FileExplorerEntry | null
renamingRow: FileExplorerRow | null
}) => {
// Local state for selection and what is opened
// diff this against new Project value that comes in
return (
<div role="presentation" className="p-px">
{rowsToRender.map((row, index, original) => {
const key = constructPath({
parentPath: row.parentPath,
name: row.name,
})
const key = row.key
const renderRow: FileExplorerRender = {
...row,
domIndex: index,
@ -82,6 +80,7 @@ export const FileExplorer = ({
key={key}
row={renderRow}
selectedRow={selectedRow}
renamingRow={renamingRow}
></FileExplorerRowElement>
)
})}
@ -89,6 +88,115 @@ export const FileExplorer = ({
)
}
function FileExplorerRowContextMenu({
itemRef,
onRename,
onDelete,
onClone,
onOpenInNewWindow,
callback,
}: FileExplorerRowContextMenuProps) {
const platform = usePlatform()
const metaKey = platform === 'macos' ? '⌘' : 'Ctrl'
return (
<ContextMenu
menuTargetElement={itemRef}
callback={callback}
items={[
<ContextMenuItem
data-testid="context-menu-rename"
onClick={onRename}
hotkey="Enter"
>
Rename
</ContextMenuItem>,
<ContextMenuItem
data-testid="context-menu-delete"
onClick={onDelete}
hotkey={metaKey + ' + Del'}
>
Delete
</ContextMenuItem>,
<ContextMenuItem
data-testid="context-menu-clone"
onClick={onClone}
hotkey=""
>
Clone
</ContextMenuItem>,
<ContextMenuItem
data-testid="context-menu-open-in-new-window"
onClick={onOpenInNewWindow}
>
Open in new window
</ContextMenuItem>,
]}
/>
)
}
function RenameForm({
row,
onSubmit,
}: {
row: FileExplorerRender
onSubmit: () => void
}) {
const inputRef = useRef<HTMLInputElement>(null)
function handleRenameSubmit(e: React.KeyboardEvent<HTMLElement>) {
if (e.key !== 'Enter') {
return
}
// TODO: Do the renaming
// newName: inputRef.current?.value || fileOrDir.name || '',
// 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 onKeyUp={handleRenameSubmit}>
<label>
<span className="sr-only">Rename file</span>
<input
data-testid="file-rename-field"
ref={inputRef}
type="text"
autoFocus
autoCapitalize="off"
autoCorrect="off"
placeholder={row.name}
className="w-full py-1 bg-transparent text-chalkboard-100 placeholder:text-chalkboard-70 dark:text-chalkboard-10 dark:placeholder:text-chalkboard-50 focus:outline-none focus:ring-0"
onKeyDown={handleKeyDown}
onBlur={onSubmit}
/>
</label>
<button className="sr-only" type="submit">
Submit
</button>
</form>
)
}
/**
* Making div soup!
* A row is a folder or a file.
@ -96,9 +204,11 @@ export const FileExplorer = ({
export const FileExplorerRowElement = ({
row,
selectedRow,
renamingRow
}: {
row: FileExplorerRender
selectedRow: FileExplorerEntry | null
renamingRow: FileExplorerRow | null
domLength: number
}) => {
const isSelected =
@ -163,50 +273,3 @@ export const FileExplorerRowElement = ({
</div>
)
}
function FileExplorerRowContextMenu({
itemRef,
onRename,
onDelete,
onClone,
onOpenInNewWindow,
callback,
}: FileExplorerRowContextMenuProps) {
const platform = usePlatform()
const metaKey = platform === 'macos' ? '⌘' : 'Ctrl'
return (
<ContextMenu
menuTargetElement={itemRef}
callback={callback}
items={[
<ContextMenuItem
data-testid="context-menu-rename"
onClick={onRename}
hotkey="Enter"
>
Rename
</ContextMenuItem>,
<ContextMenuItem
data-testid="context-menu-delete"
onClick={onDelete}
hotkey={metaKey + ' + Del'}
>
Delete
</ContextMenuItem>,
<ContextMenuItem
data-testid="context-menu-clone"
onClick={onClone}
hotkey=""
>
Clone
</ContextMenuItem>,
<ContextMenuItem
data-testid="context-menu-open-in-new-window"
onClick={onOpenInNewWindow}
>
Open in new window
</ContextMenuItem>,
]}
/>
)
}

View File

@ -23,11 +23,7 @@ const isFileExplorerEntryOpened = (
rows: { [key: string]: boolean },
entry: FileExplorerEntry
): boolean => {
const key = constructPath({
parentPath: entry.parentPath,
name: entry.name,
})
return rows[key]
return rows[entry.key]
}
/**
@ -52,6 +48,8 @@ export const ProjectExplorer = ({
// -1 is the parent container, -2 is nothing is selected
const [activeIndex, setActiveIndex] = useState<number>(NOTHING_IS_SELECTED)
const [rowsToRender, setRowsToRender] = useState<FileExplorerRow[]>([])
const [renamingRow, setRenamingRow] = useState<FileExplorerRow | null>(null)
const fileExplorerContainer = useRef(null)
const openedRowsRef = useRef(openedRows)
const rowsToRenderRef = useRef(rowsToRender)
@ -75,10 +73,7 @@ export const ProjectExplorer = ({
*/
const onRowClickCallback = (file: FileExplorerEntry, domIndex: number) => {
const newOpenedRows = { ...openedRowsRef.current }
const key = constructPath({
parentPath: file.parentPath,
name: file.name,
})
const key = file.key
const value = openedRowsRef.current[key]
newOpenedRows[key] = !value
setOpenedRows(newOpenedRows)
@ -127,7 +122,7 @@ export const ProjectExplorer = ({
const isOpen =
openedRows[
constructPath({ parentPath: child.parentPath, name: child.name })
child.key
]
const render =
(openedRows[child.parentPath] || project.name === child.parentPath) &&
@ -155,10 +150,7 @@ export const ProjectExplorer = ({
},
rowOpen: () => {
const newOpenedRows = { ...openedRowsRef.current }
const key = constructPath({
parentPath: child.parentPath,
name: child.name,
})
const key = child.key
newOpenedRows[key] = true
setOpenedRows(newOpenedRows)
},
@ -182,6 +174,7 @@ export const ProjectExplorer = ({
requestedRowsToRender.forEach((r, index) => {
r.rowContextMenu = () => {
setActiveIndex(index)
setRenamingRow(r)
}
})
@ -219,10 +212,7 @@ export const ProjectExplorer = ({
} else if (shouldCheckOpened && isEntryOpened) {
// close
const newOpenedRows = { ...openedRowsRef.current }
const key = constructPath({
parentPath: focusedEntry.parentPath,
name: focusedEntry.name,
})
const key = focusedEntry.key
const value = openedRowsRef.current[key]
newOpenedRows[key] = !value
setOpenedRows(newOpenedRows)
@ -234,10 +224,7 @@ export const ProjectExplorer = ({
} else if (shouldCheckOpened && !isEntryOpened) {
// open!
const newOpenedRows = { ...openedRowsRef.current }
const key = constructPath({
parentPath: focusedEntry.parentPath,
name: focusedEntry.name,
})
const key = focusedEntry.key
const value = openedRowsRef.current[key]
newOpenedRows[key] = !value
setOpenedRows(newOpenedRows)
@ -265,10 +252,7 @@ export const ProjectExplorer = ({
if (activeIndexRef.current >= STARTING_INDEX_TO_SELECT) {
// open close folder
const newOpenedRows = { ...openedRowsRef.current }
const key = constructPath({
parentPath: focusedEntry.parentPath,
name: focusedEntry.name,
})
const key = focusedEntry.key
const value = openedRowsRef.current[key]
newOpenedRows[key] = !value
setOpenedRows(newOpenedRows)

View File

@ -7,6 +7,7 @@ export interface FileExplorerEntry extends FileEntry {
parentPath: string
level: number
index: number
key: string
}
export interface FileExplorerRow extends FileExplorerEntry {
@ -70,6 +71,10 @@ const flattenProjectHelper = (
parentPath,
level,
index,
key: constructPath({
parentPath,
name: f.name
})
}
// keep track of the file once within the recursive list that will be built up
list.push(content)