fix: fixing bug with enter with project loader name ref

This commit is contained in:
Kevin
2025-06-24 11:44:03 -05:00
parent ebf1ec5f49
commit b733722886
3 changed files with 55 additions and 36 deletions

View File

@ -5,8 +5,7 @@ import {
type FileExplorerRow, type FileExplorerRow,
type FileExplorerRender, type FileExplorerRender,
type FileExplorerRowContextMenuProps, type FileExplorerRowContextMenuProps,
FILE_PLACEHOLDER_NAME, isRowFake,
FOLDER_PLACEHOLDER_NAME,
} from '@src/components/Explorer/utils' } from '@src/components/Explorer/utils'
import { ContextMenu, ContextMenuItem } from '@src/components/ContextMenu' import { ContextMenu, ContextMenuItem } from '@src/components/ContextMenu'
import { useRef } from 'react' import { useRef } from 'react'
@ -16,11 +15,14 @@ export const StatusDot = () => {
} }
/** /**
* Implement a dynamic spacer with rem to offset the row * A dynamic spacer that will add spaces based on the level it is in the tree
* in the tree based on the level within the tree * @param level supported be from 0 to N
* level 0 to level N * @returns
*/ */
const Spacer = (level: number) => { const Spacer = (level: number) => {
if (level < 0) {
return <div>Do not pass a number less than 0.</div>
}
const containerRemSpacing = `${level}rem` const containerRemSpacing = `${level}rem`
return level === 0 ? ( return level === 0 ? (
<div></div> <div></div>
@ -50,10 +52,7 @@ const Spacer = (level: number) => {
* Render all the rows of the file explorer in linear layout in the DOM. * Render all the rows of the file explorer in linear layout in the DOM.
* each row is rendered one after another in the same parent DOM element * each row is rendered one after another in the same parent DOM element
* rows will have aria support to understand the linear div soup layout * rows will have aria support to understand the linear div soup layout
* * Pure functional renderer, state is stored outside this component.
* what is opened and selected outside of this logic level.
*
*/ */
export const FileExplorer = ({ export const FileExplorer = ({
rowsToRender, rowsToRender,
@ -66,8 +65,6 @@ export const FileExplorer = ({
contextMenuRow: FileExplorerRow | null contextMenuRow: FileExplorerRow | null
isRenaming: boolean isRenaming: boolean
}) => { }) => {
// Local state for selection and what is opened
// diff this against new Project value that comes in
return ( return (
<div role="presentation" className="relative"> <div role="presentation" className="relative">
{rowsToRender.map((row, index, original) => { {rowsToRender.map((row, index, original) => {
@ -91,11 +88,17 @@ export const FileExplorer = ({
) )
} }
/**
* TODO Support more context menu callback functions
* cut
* paste
* copy
*
*/
function FileExplorerRowContextMenu({ function FileExplorerRowContextMenu({
itemRef, itemRef,
onRename, onRename,
onDelete, onDelete,
onClone,
onOpenInNewWindow, onOpenInNewWindow,
callback, callback,
}: FileExplorerRowContextMenuProps) { }: FileExplorerRowContextMenuProps) {
@ -134,10 +137,6 @@ function RenameForm({
if (e.key !== 'Enter') { if (e.key !== 'Enter') {
return 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 // To get out of the renaming state, without this the current file is still in renaming mode
onSubmit(e) onSubmit(e)
} }
@ -158,12 +157,7 @@ function RenameForm({
e.stopPropagation() e.stopPropagation()
} }
} }
const formattedPlaceHolder = isRowFake(row) ? '' : row.name
const hidePlaceHolderIfFakeRow =
row.name === FOLDER_PLACEHOLDER_NAME || row.name === FILE_PLACEHOLDER_NAME
? ''
: row.name
return ( return (
<form onKeyUp={handleRenameSubmit}> <form onKeyUp={handleRenameSubmit}>
<label> <label>
@ -175,7 +169,7 @@ function RenameForm({
autoFocus autoFocus
autoCapitalize="off" autoCapitalize="off"
autoCorrect="off" autoCorrect="off"
placeholder={hidePlaceHolderIfFakeRow} placeholder={formattedPlaceHolder}
className="p-1 overflow-hidden whitespace-nowrap text-ellipsis py-1 bg-transparent outline outline-primary -outline-offset-4 text-chalkboard-100 placeholder:text-chalkboard-70 dark:text-chalkboard-10 dark:placeholder:text-chalkboard-50 focus:ring-0" className="p-1 overflow-hidden whitespace-nowrap text-ellipsis py-1 bg-transparent outline outline-primary -outline-offset-4 text-chalkboard-100 placeholder:text-chalkboard-70 dark:text-chalkboard-10 dark:placeholder:text-chalkboard-50 focus:ring-0"
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}
onBlur={onSubmit} onBlur={onSubmit}
@ -203,11 +197,10 @@ export const FileExplorerRowElement = ({
contextMenuRow: FileExplorerEntry | null contextMenuRow: FileExplorerEntry | null
isRenaming: boolean isRenaming: boolean
}) => { }) => {
const rowElementRef = useRef(null)
const isSelected = const isSelected =
row.name === selectedRow?.name && row.parentPath === selectedRow?.parentPath row.name === selectedRow?.name && row.parentPath === selectedRow?.parentPath
const isIndexActive = row.domIndex === row.activeIndex const isIndexActive = row.domIndex === row.activeIndex
const rowElementRef = useRef(null)
const isContextMenuRow = contextMenuRow?.key === row.key const isContextMenuRow = contextMenuRow?.key === row.key
const isMyRowRenaming = isContextMenuRow && isRenaming const isMyRowRenaming = isContextMenuRow && isRenaming
@ -236,17 +229,18 @@ export const FileExplorerRowElement = ({
}} }}
draggable="true" draggable="true"
onDragOver={(event) => { onDragOver={(event) => {
if (!row.isOpen && row.isFolder) { // TODO
// on drag over, open! // if (!row.isOpen && row.isFolder) {
row.onOpen() // // on drag over, open!
} // row.onOpen()
event.preventDefault() // }
// event.preventDefault()
}} }}
onDragStart={(event) => { onDragStart={(event) => {
// console.log(event.target.innerText, 'onDragStart') // TODO console.log(event.target.innerText, 'onDragStart')
}} }}
onDrop={(event) => { onDrop={(event) => {
// console.log(event.target.innerText, 'onDrop') // TODO console.log(event.target.innerText, 'onDrop')
}} }}
> >
<div style={{ width: '0.5rem' }}></div> <div style={{ width: '0.5rem' }}></div>
@ -277,7 +271,6 @@ export const FileExplorerRowElement = ({
onDelete={() => { onDelete={() => {
row.onDelete() row.onDelete()
}} }}
onClone={() => {}}
onOpenInNewWindow={() => { onOpenInNewWindow={() => {
row.onOpenInNewWindow() row.onOpenInNewWindow()
}} }}

View File

@ -4,6 +4,10 @@ import { sortFilesAndDirectories } from '@src/lib/desktopFS'
import type { FileEntry } from '@src/lib/project' import type { FileEntry } from '@src/lib/project'
import { desktopSafePathJoin, joinOSPaths } from '@src/lib/paths' import { desktopSafePathJoin, joinOSPaths } from '@src/lib/paths'
/**
* Remap FileEntry data into another data structure for the Project Explorer
* This will be transformed into a DOM one called FileExplorerRow
*/
export interface FileExplorerEntry extends FileEntry { export interface FileExplorerEntry extends FileEntry {
parentPath: string parentPath: string
level: number level: number
@ -11,6 +15,9 @@ export interface FileExplorerEntry extends FileEntry {
key: string key: string
} }
/**
* Pass this to the FileExplorer to render
*/
export interface FileExplorerRow extends FileExplorerEntry { export interface FileExplorerRow extends FileExplorerEntry {
icon: CustomIconName icon: CustomIconName
name: string name: string
@ -36,6 +43,9 @@ export interface FileExplorerRow extends FileExplorerEntry {
onRenameEnd: (e: React.KeyboardEvent<HTMLElement> | null) => void onRenameEnd: (e: React.KeyboardEvent<HTMLElement> | null) => void
} }
/**
* Last conversion for linear rendering of DOM elements, we need the index.
*/
export interface FileExplorerRender extends FileExplorerRow { export interface FileExplorerRender extends FileExplorerRow {
domIndex: number domIndex: number
domLength: number domLength: number
@ -45,11 +55,14 @@ export interface FileExplorerRowContextMenuProps {
itemRef: React.RefObject<HTMLElement> itemRef: React.RefObject<HTMLElement>
onRename: () => void onRename: () => void
onDelete: () => void onDelete: () => void
onClone: () => void
onOpenInNewWindow: () => void onOpenInNewWindow: () => void
callback: () => void callback: () => void
} }
/**
* Create a key that can be used for a hash table for opened rows
* this is also used for key={} in React.
*/
export const constructPath = ({ export const constructPath = ({
parentPath, parentPath,
name, name,
@ -162,6 +175,14 @@ export const addPlaceHoldersForNewFileAndFolder = (
children.push(placeHolderFileEntry) children.push(placeHolderFileEntry)
} }
export const isRowFake = (
row: FileExplorerRender | FileExplorerRow | FileExplorerEntry
): boolean => {
return (
row.name === FOLDER_PLACEHOLDER_NAME || row.name === FILE_PLACEHOLDER_NAME
)
}
// Used for focused which is different from the selection when you mouse click. // Used for focused which is different from the selection when you mouse click.
export const NOTHING_IS_SELECTED: number = -2 export const NOTHING_IS_SELECTED: number = -2
export const CONTAINER_IS_SELECTED: number = -1 export const CONTAINER_IS_SELECTED: number = -1

View File

@ -4,6 +4,7 @@ import {
useEffect, useEffect,
type MouseEventHandler, type MouseEventHandler,
type ReactNode, type ReactNode,
useRef,
} from 'react' } from 'react'
import type { ContextFrom } from 'xstate' import type { ContextFrom } from 'xstate'
@ -145,10 +146,13 @@ export const sidebarPanes: SidebarPane[] = [
Content: (props: { id: SidebarType; onClose: () => void }) => { Content: (props: { id: SidebarType; onClose: () => void }) => {
const projects = useFolders() const projects = useFolders()
const loaderData = useRouteLoaderData(PATHS.FILE) as IndexLoaderData const loaderData = useRouteLoaderData(PATHS.FILE) as IndexLoaderData
const projectNameRef = useRef(loaderData.project?.name)
const [theProject, setTheProject] = useState<Project | null>(null) const [theProject, setTheProject] = useState<Project | null>(null)
const { project, file } = loaderData const { project, file } = loaderData
const settings = useSettings() const settings = useSettings()
useEffect(() => { useEffect(() => {
projectNameRef.current = loaderData?.project?.name
// Have no idea why the project loader data doesn't have the children from the ls on disk // Have no idea why the project loader data doesn't have the children from the ls on disk
// That means it is a different object or cached incorrectly? // That means it is a different object or cached incorrectly?
if (!project || !file) { if (!project || !file) {
@ -186,14 +190,15 @@ export const sidebarPanes: SidebarPane[] = [
// Only open the file if it is a kcl file. // Only open the file if it is a kcl file.
if ( if (
loaderData?.project?.name && projectNameRef.current &&
entry.children == null && entry.children == null &&
entry.path.endsWith(FILE_EXT) entry.path.endsWith(FILE_EXT)
) { ) {
systemIOActor.send({ systemIOActor.send({
type: SystemIOMachineEvents.navigateToFile, type: SystemIOMachineEvents.navigateToFile,
data: { data: {
requestedProjectName: loaderData?.project?.name, requestedProjectName: projectNameRef.current,
requestedFileName: requestedFileName, requestedFileName: requestedFileName,
}, },
}) })