mirror of
https://github.com/stackblitz/bolt.new
synced 2025-02-06 04:48:04 +00:00
copyPath and copyRelativePath for files and folders
This commit is contained in:
parent
eb36ec6170
commit
13a15e9a3d
@ -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,14 +253,25 @@ 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 (
|
||||||
|
<FileContextMenu onCopyPath={onCopyPath} onCopyRelativePath={onCopyRelativePath}>
|
||||||
<NodeButton
|
<NodeButton
|
||||||
className={classNames('group', {
|
className={classNames('group', {
|
||||||
'bg-transparent hover:bg-bolt-elements-item-backgroundActive text-bolt-elements-item-contentDefault': !selected,
|
'bg-transparent hover:bg-bolt-elements-item-backgroundActive text-bolt-elements-item-contentDefault':
|
||||||
|
!selected,
|
||||||
'bg-bolt-elements-item-backgroundAccent text-bolt-elements-item-contentAccent': selected,
|
'bg-bolt-elements-item-backgroundAccent text-bolt-elements-item-contentAccent': selected,
|
||||||
})}
|
})}
|
||||||
depth={depth}
|
depth={depth}
|
||||||
@ -251,6 +289,7 @@ function File({ file: { depth, name }, onClick, selected, unsavedChanges = false
|
|||||||
{unsavedChanges && <span className="i-ph:circle-fill scale-68 shrink-0 text-orange-500" />}
|
{unsavedChanges && <span className="i-ph:circle-fill scale-68 shrink-0 text-orange-500" />}
|
||||||
</div>
|
</div>
|
||||||
</NodeButton>
|
</NodeButton>
|
||||||
|
</FileContextMenu>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user