copyPath and copyRelativePath for files and folders

This commit is contained in:
Rob Koch 2024-12-08 15:22:46 -08:00
parent eb36ec6170
commit 13a15e9a3d

View File

@ -111,6 +111,22 @@ export const FileTree = memo(
}); });
}; };
const onCopyPath = (fileOrFolder: FileNode | FolderNode) => {
try {
navigator.clipboard.writeText(fileOrFolder.fullPath);
} catch (error) {
logger.error(error);
}
};
const onCopyRelativePath = (fileOrFolder: FileNode | FolderNode) => {
try {
navigator.clipboard.writeText(fileOrFolder.fullPath.substring((rootFolder || '').length));
} catch (error) {
logger.error(error);
}
};
return ( return (
<div className={classNames('text-sm', className, 'overflow-y-auto')}> <div className={classNames('text-sm', className, 'overflow-y-auto')}>
{filteredFileList.map((fileOrFolder) => { {filteredFileList.map((fileOrFolder) => {
@ -122,6 +138,12 @@ export const FileTree = memo(
selected={selectedFile === fileOrFolder.fullPath} selected={selectedFile === fileOrFolder.fullPath}
file={fileOrFolder} file={fileOrFolder}
unsavedChanges={unsavedFiles?.has(fileOrFolder.fullPath)} unsavedChanges={unsavedFiles?.has(fileOrFolder.fullPath)}
onCopyPath={() => {
onCopyPath(fileOrFolder);
}}
onCopyRelativePath={() => {
onCopyRelativePath(fileOrFolder);
}}
onClick={() => { onClick={() => {
onFileSelect?.(fileOrFolder.fullPath); onFileSelect?.(fileOrFolder.fullPath);
}} }}
@ -135,6 +157,12 @@ export const FileTree = memo(
folder={fileOrFolder} folder={fileOrFolder}
selected={allowFolderSelection && selectedFile === fileOrFolder.fullPath} selected={allowFolderSelection && selectedFile === fileOrFolder.fullPath}
collapsed={collapsedFolders.has(fileOrFolder.fullPath)} collapsed={collapsedFolders.has(fileOrFolder.fullPath)}
onCopyPath={() => {
onCopyPath(fileOrFolder);
}}
onCopyRelativePath={() => {
onCopyRelativePath(fileOrFolder);
}}
onClick={() => { onClick={() => {
toggleCollapseState(fileOrFolder.fullPath); toggleCollapseState(fileOrFolder.fullPath);
}} }}
@ -157,23 +185,30 @@ interface FolderProps {
folder: FolderNode; folder: FolderNode;
collapsed: boolean; collapsed: boolean;
selected?: boolean; selected?: boolean;
onCopyPath: () => void;
onCopyRelativePath: () => void;
onClick: () => void; onClick: () => void;
} }
interface FolderContextMenuProps { interface FolderContextMenuProps {
onCopyPath?: () => void;
onCopyRelativePath?: () => void;
children: ReactNode; children: ReactNode;
} }
function ContextMenuItem({ children }: { children: ReactNode }) { function ContextMenuItem({ onSelect, children }: { onSelect?: () => void; children: ReactNode }) {
return ( return (
<ContextMenu.Item className="flex items-center gap-2 px-2 py-1.5 outline-0 text-sm text-bolt-elements-textPrimary cursor-pointer ws-nowrap text-bolt-elements-item-contentDefault hover:text-bolt-elements-item-contentActive hover:bg-bolt-elements-item-backgroundActive rounded-md"> <ContextMenu.Item
onSelect={onSelect}
className="flex items-center gap-2 px-2 py-1.5 outline-0 text-sm text-bolt-elements-textPrimary cursor-pointer ws-nowrap text-bolt-elements-item-contentDefault hover:text-bolt-elements-item-contentActive hover:bg-bolt-elements-item-backgroundActive rounded-md"
>
<span className="size-4 shrink-0"></span> <span className="size-4 shrink-0"></span>
<span>{children}</span> <span>{children}</span>
</ContextMenu.Item> </ContextMenu.Item>
); );
} }
function FolderContextMenu({ children }: FolderContextMenuProps) { function FileContextMenu({ onCopyPath, onCopyRelativePath, children }: FolderContextMenuProps) {
return ( return (
<ContextMenu.Root> <ContextMenu.Root>
<ContextMenu.Trigger>{children}</ContextMenu.Trigger> <ContextMenu.Trigger>{children}</ContextMenu.Trigger>
@ -183,16 +218,8 @@ function FolderContextMenu({ children }: FolderContextMenuProps) {
className="border border-bolt-elements-borderColor rounded-md z-context-menu bg-bolt-elements-background-depth-1 dark:bg-bolt-elements-background-depth-2 data-[state=open]:animate-in animate-duration-100 data-[state=open]:fade-in-0 data-[state=open]:zoom-in-98 w-56" className="border border-bolt-elements-borderColor rounded-md z-context-menu bg-bolt-elements-background-depth-1 dark:bg-bolt-elements-background-depth-2 data-[state=open]:animate-in animate-duration-100 data-[state=open]:fade-in-0 data-[state=open]:zoom-in-98 w-56"
> >
<ContextMenu.Group className="p-1 border-b-px border-solid border-bolt-elements-borderColor"> <ContextMenu.Group className="p-1 border-b-px border-solid border-bolt-elements-borderColor">
<ContextMenuItem>New file...</ContextMenuItem> <ContextMenuItem onSelect={onCopyPath}>Copy path</ContextMenuItem>
<ContextMenuItem>New folder...</ContextMenuItem> <ContextMenuItem onSelect={onCopyRelativePath}>Copy relative path</ContextMenuItem>
</ContextMenu.Group>
<ContextMenu.Group className="p-1 border-b-px border-solid border-bolt-elements-borderColor">
<ContextMenuItem>Copy path</ContextMenuItem>
<ContextMenuItem>Copy relative path</ContextMenuItem>
</ContextMenu.Group>
<ContextMenu.Group className="p-1 border-b-px border-solid border-bolt-elements-borderColor">
<ContextMenuItem>Rename...</ContextMenuItem>
<ContextMenuItem>Delete</ContextMenuItem>
</ContextMenu.Group> </ContextMenu.Group>
</ContextMenu.Content> </ContextMenu.Content>
</ContextMenu.Portal> </ContextMenu.Portal>
@ -200,9 +227,9 @@ function FolderContextMenu({ children }: FolderContextMenuProps) {
); );
} }
function Folder({ folder, collapsed, selected = false, onClick }: FolderProps) { function Folder({ folder, collapsed, selected = false, onCopyPath, onCopyRelativePath, onClick }: FolderProps) {
return ( return (
<FolderContextMenu> <FileContextMenu onCopyPath={onCopyPath} onCopyRelativePath={onCopyRelativePath}>
<NodeButton <NodeButton
className={classNames('group', { className={classNames('group', {
'bg-transparent text-bolt-elements-item-contentDefault hover:text-bolt-elements-item-contentActive hover:bg-bolt-elements-item-backgroundActive': 'bg-transparent text-bolt-elements-item-contentDefault hover:text-bolt-elements-item-contentActive hover:bg-bolt-elements-item-backgroundActive':
@ -218,7 +245,7 @@ function Folder({ folder, collapsed, selected = false, onClick }: FolderProps) {
> >
{folder.name} {folder.name}
</NodeButton> </NodeButton>
</FolderContextMenu> </FileContextMenu>
); );
} }
@ -226,31 +253,43 @@ interface FileProps {
file: FileNode; file: FileNode;
selected: boolean; selected: boolean;
unsavedChanges?: boolean; unsavedChanges?: boolean;
onCopyPath: () => void;
onCopyRelativePath: () => void;
onClick: () => void; onClick: () => void;
} }
function File({ file: { depth, name }, onClick, selected, unsavedChanges = false }: FileProps) { function File({
file: { depth, name },
onClick,
onCopyPath,
onCopyRelativePath,
selected,
unsavedChanges = false,
}: FileProps) {
return ( return (
<NodeButton <FileContextMenu onCopyPath={onCopyPath} onCopyRelativePath={onCopyRelativePath}>
className={classNames('group', { <NodeButton
'bg-transparent hover:bg-bolt-elements-item-backgroundActive text-bolt-elements-item-contentDefault': !selected, className={classNames('group', {
'bg-bolt-elements-item-backgroundAccent text-bolt-elements-item-contentAccent': selected, 'bg-transparent hover:bg-bolt-elements-item-backgroundActive text-bolt-elements-item-contentDefault':
})} !selected,
depth={depth} 'bg-bolt-elements-item-backgroundAccent text-bolt-elements-item-contentAccent': selected,
iconClasses={classNames('i-ph:file-duotone scale-98', { })}
'group-hover:text-bolt-elements-item-contentActive': !selected, depth={depth}
})} iconClasses={classNames('i-ph:file-duotone scale-98', {
onClick={onClick}
>
<div
className={classNames('flex items-center', {
'group-hover:text-bolt-elements-item-contentActive': !selected, 'group-hover:text-bolt-elements-item-contentActive': !selected,
})} })}
onClick={onClick}
> >
<div className="flex-1 truncate pr-2">{name}</div> <div
{unsavedChanges && <span className="i-ph:circle-fill scale-68 shrink-0 text-orange-500" />} className={classNames('flex items-center', {
</div> 'group-hover:text-bolt-elements-item-contentActive': !selected,
</NodeButton> })}
>
<div className="flex-1 truncate pr-2">{name}</div>
{unsavedChanges && <span className="i-ph:circle-fill scale-68 shrink-0 text-orange-500" />}
</div>
</NodeButton>
</FileContextMenu>
); );
} }