diff --git a/webui/src/components/BreadcrumbLinks.tsx b/webui/src/components/BreadcrumbLinks.tsx index 781af74..4a687f8 100644 --- a/webui/src/components/BreadcrumbLinks.tsx +++ b/webui/src/components/BreadcrumbLinks.tsx @@ -1,8 +1,7 @@ import { Link } from 'expo-router'; -// @ts-expect-error -import CheckIcon from 'lucide-react/dist/esm/icons/check'; -import { ComponentProps, Fragment, PropsWithChildren, useCallback, useMemo, useState } from 'react'; +import { type ComponentProps, Fragment, useMemo } from 'react'; +import { FilePathMenu } from '~/components/FilePathMenu'; import { Breadcrumb, BreadcrumbItem, @@ -11,8 +10,6 @@ import { BreadcrumbPage, BreadcrumbSeparator, } from '~/ui/Breadcrumb'; -import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuTrigger } from '~/ui/Menu'; -import { TooltipProvider, Tooltip, TooltipTrigger, TooltipContent } from '~/ui/Tooltip'; import { type PartialAtlasBundle } from '~core/data/types'; type BreadcrumbLinksProps = { @@ -41,11 +38,11 @@ export function BreadcrumbLinks(props: BreadcrumbLinksProps) { {!link.href ? ( - + {link.label} - + ) : ( - + - + )} @@ -64,56 +61,7 @@ export function BreadcrumbLinks(props: BreadcrumbLinksProps) { ); } -type BreadcrumbLinkMenuProps = PropsWithChildren<{ - bundle: PartialAtlasBundle; - link: BreadcrumbLinkItem; -}>; - -function BreadcrumbLinkMenu(props: BreadcrumbLinkMenuProps) { - const [tooltipContent, setTooltipContent] = useState(null); - - const showCopyTooltip = useCallback((content: string) => { - setTooltipContent(content); - setTimeout(() => setTooltipContent(null), 2000); - }, []); - - const onCopyRelativePath = useCallback(() => { - navigator.clipboard.writeText(props.link.filePath); - showCopyTooltip(`Relative ${props.link.type} path copied`); - }, [props.link.type, props.link.filePath, showCopyTooltip]); - - const onCopyAbsolutePath = useCallback(() => { - navigator.clipboard.writeText(`${props.bundle.sharedRoot}/${props.link.filePath}`); - showCopyTooltip(`Absolute ${props.link.type} path copied`); - }, [props.link.type, props.link.filePath, props.bundle.sharedRoot, showCopyTooltip]); - - return ( - - - - - {props.children} - - - {tooltipContent} - - - - - Copy relative {props.link.type} path - - - Copy absolute {props.link.type} path - - - - - - ); -} - -type BreadcrumbLinkItem = { - type: 'folder' | 'file'; +type BreadcrumbLinkItem = ComponentProps & { key: string; label: string; filePath: string; @@ -123,16 +71,23 @@ type BreadcrumbLinkItem = { function getBreadcrumbLinks(props: BreadcrumbLinksProps): BreadcrumbLinkItem[] { return props.path.split('/').map((label, index, breadcrumbs) => { const isLastSegment = index === breadcrumbs.length - 1; + const filePath = breadcrumbs.slice(0, index + 1).join('/'); const breadcrumb: BreadcrumbLinkItem = { - label, - type: 'file', key: `${index}-${label}`, - filePath: breadcrumbs.slice(0, index + 1).join('/'), + label, + filePath, + relative: { + path: filePath, + label: isLastSegment ? 'Copy relative file path' : 'Copy relative folder path', + }, + absolute: { + path: `${props.bundle.sharedRoot}/${filePath}`, + label: isLastSegment ? 'Copy absolute file path' : 'Copy absolute folder path', + }, }; // NOTE(cedric): a bit of a workaround to avoid linking to the current page, might need to change this if (!isLastSegment || !label.includes('.')) { - breadcrumb.type = 'folder'; breadcrumb.href = { pathname: '/(atlas)/[bundle]/folders/[path]', params: { bundle: props.bundle.id, path: breadcrumb.filePath }, diff --git a/webui/src/components/FilePathMenu.tsx b/webui/src/components/FilePathMenu.tsx new file mode 100644 index 0000000..58a8689 --- /dev/null +++ b/webui/src/components/FilePathMenu.tsx @@ -0,0 +1,60 @@ +// @ts-expect-error +import CheckIcon from 'lucide-react/dist/esm/icons/check'; +import { PropsWithChildren, useState, useCallback } from 'react'; + +import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuTrigger } from '~/ui/Menu'; +import { TooltipProvider, Tooltip, TooltipTrigger, TooltipContent } from '~/ui/Tooltip'; + +type FilePath = { + path: string; + label?: string; + copy?: string; +}; + +type FilePathMenuProps = PropsWithChildren<{ + absolute: FilePath; + relative: FilePath; +}>; + +export function FilePathMenu(props: FilePathMenuProps) { + const [tooltipContent, setTooltipContent] = useState(null); + + const showCopyTooltip = useCallback((content: string) => { + setTooltipContent(content); + setTimeout(() => setTooltipContent(null), 2000); + }, []); + + const onCopyRelativePath = useCallback(() => { + navigator.clipboard.writeText(props.relative.path); + showCopyTooltip(props.relative.copy ?? 'Relative path copied'); + }, [props.relative.path, props.relative.copy, showCopyTooltip]); + + const onCopyAbsolutePath = useCallback(() => { + navigator.clipboard.writeText(props.absolute.path); + showCopyTooltip(props.absolute.copy ?? 'Absolute path copied'); + }, [props.absolute.path, props.absolute.copy, showCopyTooltip]); + + return ( + + + + + {props.children} + + + {tooltipContent} + + + + + {props.relative.label ?? 'Copy relative path'} + + + {props.absolute.label ?? 'Copy absolute path'} + + + + + + ); +}