I have this tree like structure where in the user can create pages, sub pages, delete them and even edit them and all works great other than this one weird bug everytime i create a a nested page its scrolls the page down to the latest created node/page which is messing up the UX and i want to avoid that scroll
Tried this use effect if it could help ->
useEffect(() => {
const x = document.getElementById('addNewPage')
x.scrollIntoView({ behavior: "smooth" })
x.focus()
}, [isOpen])
but the use effect did not help so added this same x.scrollIntoView into the add file Fn and neither did that work
CODE ->
const FileTree = React.memo(() => {
const [files, setFiles] = useState([]);
const [editing, setEditing] = useState(null);
const [newFileName, setNewFileName] = useState('');
// const [activity, setActivity] = useState('')
const handleAddFile = (parentIndex = null) => {
if (newFileName.trim() === '') return;
const newFile = { name: newFileName, children: [], id: Date.now() };
if (parentIndex === null) {
setFiles([...files, newFile]);
} else {
const newFiles = [...files];
const addNestedFile = (file, idx) => {
if (idx === parentIndex) {
file.children.push(newFile);
} else {
file.children.forEach((child, childIdx) => {
addNestedFile(child, `${idx}-${childIdx}`);
});
}
};
newFiles.forEach((file, idx) => addNestedFile(file, idx.toString()));
setFiles(newFiles);
}
setNewFileName('');
setEditing(null);
};
const handleDeleteFile = (index) => {
setFiles(files.filter((_, i) => i !== index));
};
const handleDeleteNestedFile = (parentIndex, index) => {
const newFiles = [...files];
const deleteNestedFile = (file, idx) => {
if (idx === parentIndex) {
file.children = file.children.filter((_, i) => i !== index);
} else {
file.children.forEach((child, childIdx) => {
deleteNestedFile(child, `${idx}-${childIdx}`);
});
}
};
newFiles.forEach((file, idx) => deleteNestedFile(file, idx.toString()));
setFiles(newFiles);
};
const [activelyEditingComponent, setActivelyEditingComponent] = useState(null); // not using
const [nestedIndexForEditing, setNestedIndexForEditing] = useState(null);
const [editedFileName, setEditedFileName] = useState('');
const handleSaveEdit = () => {
const tempFiles = [...files];
const nestedIndex = nestedIndexForEditing.split('-');
const updateFileName = (file, indexes) => {
if (indexes.length === 1) {
file.children[indexes[0]].name = editedFileName;
} else {
updateFileName(file.children[indexes[0]], indexes.slice(1));
}
};
if (nestedIndex.length === 1) {
tempFiles[nestedIndex[0]].name = editedFileName;
} else {
updateFileName(tempFiles[nestedIndex[0]], nestedIndex.slice(1));
}
setFiles(tempFiles);
setActivelyEditingComponent(null);
setNestedIndexForEditing(null);
setEditedFileName('');
setIsEditingModalOpen(false)
};
const [isOpen, setIsOpen] = useState(false)
const [accessedFrom, setAccessedFrom] = useState('')
const [isEditingModalOpen, setIsEditingModalOpen] = useState(false)
const renderFileTree = (files, parentIndex = null) => {
return files.map((file, index) => {
const currentIndex = parentIndex === null ? index.toString() : `${parentIndex}-${index}`;
const hasChildren = file.children.length > 0;
return (
<div key={currentIndex} className={`flex ${editing && "gap-4"} ${!hasChildren && "gap-12"} ${hasChildren ? "items-start" : "items-center"}`} style={{ marginLeft: parentIndex !== null ? 20 : 0, borderLeft: parentIndex !== null ? '2px solid #ccc' : 'none', paddingLeft: 10 }}>
{hasChildren ? (
<details className=''>
<summary
onMouseEnter={() => setEditing(currentIndex)}
className='my-2 border items-center gap-1 flex w-36 border-white rounded-md px-2 py-1 cursor-pointer hover:text-gray-400 hover:border-gray-400 transition-all ease-in duration-100'
>
<FileIcon className=' w-5 h-5' />
<span className='w-[88%] overflow-hidden whitespace-nowrap text-ellipsis'> {file.name}</span>
{
hasChildren && (
<ChevronDown className='flex w-6 h-6' />
)
}
</summary>
{renderFileTree(file.children, currentIndex)}
</details>
) : (
<div
onMouseEnter={() => setEditing(currentIndex)}
className='my-2 border items-center gap-1 flex w-36 border-white rounded-md px-2 py-1 cursor-pointer hover:text-gray-400 hover:border-gray-400 transition-all ease-in duration-100'
>
<FileIcon className='flex w-5 h-5' />
<span className='w-[88%] overflow-hidden whitespace-nowrap text-ellipsis'> {file.name}</span>
</div>
)}
{editing === currentIndex && (
<div className={`flex slideIconToTop items-center gap-1 w-fit bg-black rounded-md border border-gray-500 px-[6px] pb-[3px] ${hasChildren ? "pt-[4px] mt-[6px]" : "pt-1"} z-10`}>
{/* <div className={`flex slideIconToTop items-center gap-1 ml-3 w-fit ${hasChildren && "pt-[10px]"}`}> */}
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger>
<button className='rounded-md w-7 h-7 text-xl flex justify-center items-center hover:text-slate-400 transition-all ease-in duration-100' onClick={() => {
// setEditing(`nested-${currentIndex}`)
setIsOpen(true)
// setActivity('Add')
setAccessedFrom(currentIndex)
}}>
<PlusIcon />
</button>
</TooltipTrigger>
<TooltipContent>
<p>create page</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger>
<button className='rounded-md w-7 h-7 text-xl flex justify-center items-center hover:text-slate-400 transition-all ease-in duration-100' onClick={() => parentIndex === null ? handleDeleteFile(index) : handleDeleteNestedFile(parentIndex, index)}>
<RxCross2 />
</button>
</TooltipTrigger>
<TooltipContent>
<p>delete page</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger>
<span
className='rounded-md w-6 h-6 p-[4px] text-xl cursor-pointer flex justify-center items-center hover:text-slate-400 transition-all ease-in duration-100'
onClick={() => {
setActivelyEditingComponent(file.id);
setNestedIndexForEditing(currentIndex);
setEditedFileName(file.name);
setIsEditingModalOpen(true)
}}
>
<PencilIcon />
</span>
</TooltipTrigger>
<TooltipContent>
<p>Edit page</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
)}
</div>
);
});
};
return (
<div className='pt-8 ml-8 fixed left-0 top-24 z-10 h-[calc(100vh-90px)] noScrollbar overflow-y-auto'>
<div className='flex items-center gap-4'>
<Button onClick={() => setEditing('new')}>Add New Page</Button>
<div className="flex items-center gap-2">
<p className="text-sm font-semibold">OR</p>
<Button variant="secondary" className="underline">Import your data</Button>
</div>
</div>
{editing === 'new' && (
<div style={{ marginTop: 10 }} className='flex items-center gap-2 ml-[1px]'>
<input
value={newFileName}
className='pl-3 pr-2 rounded-md h-9'
onChange={(e) => setNewFileName(e.target.value)}
placeholder="Enter Page Name"
ref={(input) => input && input.focus()}
onKeyDown={(e) => {
if (e.key === 'Enter') {
handleAddFile();
}
}}
/>
<button className='rounded-md w-7 h-7 text-xl flex justify-center items-center hover:bg-slate-700 transition-all ease-in duration-100' onClick={() => setEditing(null)}>
<RxCross2 />
</button>
</div>
)}
<div style={{ marginTop: 20 }}>
{renderFileTree(files)}
</div>
<AddFileNameModal setNewFileName={setNewFileName} newFileName={newFileName} isOpen={isOpen} setIsOpen={setIsOpen} accessedFrom={accessedFrom} handleAddFile={handleAddFile} />
<EditFileNameModal handleSaveEdit={handleSaveEdit} editedFileName={editedFileName} setEditedFileName={setEditedFileName} setNestedIndexForEditing={setNestedIndexForEditing} setActivelyEditingComponent={setActivelyEditingComponent} isEditingModalOpen={isEditingModalOpen} setIsEditingModalOpen={setIsEditingModalOpen} />
</div>
);
});
export default FileTree;
const AddFileNameModal = React.memo(({ isOpen, setIsOpen, accessedFrom, handleAddFile, newFileName, setNewFileName, actiivity = 'Add' }) => {
// actiivity -> add || edit
return (
<AlertDialog modal={false} open={isOpen}>
<AlertDialogTrigger></AlertDialogTrigger>
<AlertDialogContent onEscapeKeyDown={() => setIsOpen(false)} >
<AlertDialogHeader>
<p className='text-lg'>{actiivity} Page </p>
<AlertDialogDescription>
<input
value={newFileName}
className='pl-3 pr-2 rounded-md h-11 !border-gray-500 border w-full !focus:border-gray-500'
onChange={(e) => setNewFileName(e.target.value)}
placeholder="Page name"
ref={(input) => input && input.focus()}
onKeyDown={(e) => {
if (e.key === 'Enter') {
handleAddFile(accessedFrom);
setIsOpen(false)
}
}}
/>
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel onClick={() => setIsOpen(false)}>Cancel</AlertDialogCancel>
<AlertDialogAction onClick={() => setIsOpen(false)}>Continue</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
)
})
const EditFileNameModal = React.memo(({
isEditingModalOpen,
setIsEditingModalOpen,
setEditedFileName,
setNestedIndexForEditing,
setActivelyEditingComponent,
// setEditedFileName,
editedFileName,
handleSaveEdit
}) => {
return (
<AlertDialog open={isEditingModalOpen}>
<AlertDialogTrigger></AlertDialogTrigger>
<AlertDialogContent onEscapeKeyDown={() => setIsEditingModalOpen(false)} >
<AlertDialogHeader>
<p className='text-lg'>Rename Page</p>
<AlertDialogDescription>
<div className='flex items-center gap-2'>
<input
value={editedFileName}
className='pl-3 pr-2 rounded-md h-9 !border-gray-500 border !focus:border-gray-500 w-full'
onChange={(e) => setEditedFileName(e.target.value)}
placeholder="Edit file name"
ref={(input) => input && input.focus()}
onKeyDown={(e) => {
if (e.key === 'Enter') {
handleSaveEdit();
}
}}
/>
{/* <span className='rounded-md w-7 h-7 text-xl flex justify-center items-center hover:text-slate-400 transition-all ease-in duration-100' onClick={handleSaveEdit}>
<SaveIcon />
</span> */}
{/* <span className='rounded-md w-7 h-7 text-xl flex justify-center items-center hover:text-slate-400 transition-all ease-in duration-100' onClick={() => setActivelyEditingComponent(null)}>
<RxCross2 />
</span> */}
</div>
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel onClick={() => setIsEditingModalOpen(false)}>Cancel</AlertDialogCancel>
<AlertDialogAction onClick={() => setIsEditingModalOpen(false)}>Continue</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
)
})
can somebody please help me figure this out 🙂
and no its not a window.scroll to top as its not scrolling the entire page/window but just in that container the glitch takes place