mirror of
https://github.com/langgenius/dify.git
synced 2026-05-05 00:57:04 +08:00
feat: Implement Dropdown and Menu components for breadcrumb navigation in Online Drive
This commit is contained in:
parent
1ff608dfa9
commit
1bd664e655
@ -0,0 +1,61 @@
|
|||||||
|
import React, { useCallback, useState } from 'react'
|
||||||
|
import {
|
||||||
|
PortalToFollowElem,
|
||||||
|
PortalToFollowElemContent,
|
||||||
|
PortalToFollowElemTrigger,
|
||||||
|
} from '@/app/components/base/portal-to-follow-elem'
|
||||||
|
import { RiMoreFill } from '@remixicon/react'
|
||||||
|
import cn from '@/utils/classnames'
|
||||||
|
import Menu from './menu'
|
||||||
|
|
||||||
|
type DropdownProps = {
|
||||||
|
startIndex: number
|
||||||
|
breadcrumbs: string[]
|
||||||
|
onBreadcrumbClick: (index: number) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const Dropdown = ({
|
||||||
|
startIndex,
|
||||||
|
breadcrumbs,
|
||||||
|
onBreadcrumbClick,
|
||||||
|
}: DropdownProps) => {
|
||||||
|
const [open, setOpen] = useState(false)
|
||||||
|
|
||||||
|
const handleTrigger = useCallback(() => {
|
||||||
|
setOpen(prev => !prev)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PortalToFollowElem
|
||||||
|
open={open}
|
||||||
|
onOpenChange={setOpen}
|
||||||
|
placement='bottom-start'
|
||||||
|
offset={{
|
||||||
|
mainAxis: 4,
|
||||||
|
crossAxis: -13,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<PortalToFollowElemTrigger onClick={handleTrigger}>
|
||||||
|
<button
|
||||||
|
type='button'
|
||||||
|
className={cn(
|
||||||
|
'rounded-md p-1',
|
||||||
|
open ? 'bg-state-base-hover' : 'hover:bg-state-base-hover',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<RiMoreFill className='size-4 text-text-tertiary' />
|
||||||
|
</button>
|
||||||
|
</PortalToFollowElemTrigger>
|
||||||
|
<PortalToFollowElemContent className='z-[11]'>
|
||||||
|
<Menu
|
||||||
|
breadcrumbs={breadcrumbs}
|
||||||
|
startIndex={startIndex}
|
||||||
|
onBreadcrumbClick={onBreadcrumbClick}
|
||||||
|
/>
|
||||||
|
</PortalToFollowElemContent>
|
||||||
|
<span className='system-xs-regular text-divider-deep'>/</span>
|
||||||
|
</PortalToFollowElem>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default React.memo(Dropdown)
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
import React, { useCallback } from 'react'
|
||||||
|
|
||||||
|
type ItemProps = {
|
||||||
|
name: string
|
||||||
|
index: number
|
||||||
|
onBreadcrumbClick: (index: number) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const Item = ({
|
||||||
|
name,
|
||||||
|
index,
|
||||||
|
onBreadcrumbClick,
|
||||||
|
}: ItemProps) => {
|
||||||
|
const handleClick = useCallback(() => {
|
||||||
|
onBreadcrumbClick(index)
|
||||||
|
}, [index, onBreadcrumbClick])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className='system-md-regular rounded-lg px-3 py-1.5 text-text-secondary hover:bg-state-base-hover'
|
||||||
|
onClick={handleClick}
|
||||||
|
>
|
||||||
|
{name}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default React.memo(Item)
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import Item from './item'
|
||||||
|
|
||||||
|
type MenuProps = {
|
||||||
|
breadcrumbs: string[]
|
||||||
|
startIndex: number
|
||||||
|
onBreadcrumbClick: (index: number) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const Menu = ({
|
||||||
|
breadcrumbs,
|
||||||
|
startIndex,
|
||||||
|
onBreadcrumbClick,
|
||||||
|
}: MenuProps) => {
|
||||||
|
return (
|
||||||
|
<div className='flex w-[136px] flex-col rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 shadow-lg shadow-shadow-shadow-5 backdrop-blur-[5px]'>
|
||||||
|
{breadcrumbs.map((breadcrumb, index) => {
|
||||||
|
return (
|
||||||
|
<Item
|
||||||
|
name={breadcrumb}
|
||||||
|
index={startIndex + index}
|
||||||
|
onBreadcrumbClick={onBreadcrumbClick}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default React.memo(Menu)
|
||||||
@ -3,6 +3,7 @@ import { useTranslation } from 'react-i18next'
|
|||||||
import { useDataSourceStore } from '../../../../store'
|
import { useDataSourceStore } from '../../../../store'
|
||||||
import Bucket from './bucket'
|
import Bucket from './bucket'
|
||||||
import BreadcrumbItem from './item'
|
import BreadcrumbItem from './item'
|
||||||
|
import Dropdown from './dropdown'
|
||||||
|
|
||||||
type BreadcrumbsProps = {
|
type BreadcrumbsProps = {
|
||||||
prefix: string[]
|
prefix: string[]
|
||||||
@ -55,11 +56,11 @@ const Breadcrumbs = ({
|
|||||||
}, [setFileList, setPrefix, setSelectedFileList])
|
}, [setFileList, setPrefix, setSelectedFileList])
|
||||||
|
|
||||||
const handleClickBreadcrumb = useCallback((index: number) => {
|
const handleClickBreadcrumb = useCallback((index: number) => {
|
||||||
const newPrefix = breadcrumbs.prefixBreadcrumbs.slice(0, index - 1)
|
const newPrefix = prefix.slice(0, index - 1)
|
||||||
setFileList([])
|
setFileList([])
|
||||||
setSelectedFileList([])
|
setSelectedFileList([])
|
||||||
setPrefix(newPrefix)
|
setPrefix(newPrefix)
|
||||||
}, [breadcrumbs.prefixBreadcrumbs, setFileList, setPrefix, setSelectedFileList])
|
}, [prefix, setFileList, setPrefix, setSelectedFileList])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex grow items-center py-1'>
|
<div className='flex grow items-center py-1'>
|
||||||
@ -106,6 +107,33 @@ const Breadcrumbs = ({
|
|||||||
})}
|
})}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
{breadcrumbs.needCollapsed && (
|
||||||
|
<>
|
||||||
|
{breadcrumbs.prefixBreadcrumbs.map((breadcrumb, index) => {
|
||||||
|
return (
|
||||||
|
<BreadcrumbItem
|
||||||
|
key={`${breadcrumb}-${index}`}
|
||||||
|
index={index}
|
||||||
|
handleClick={handleClickBreadcrumb}
|
||||||
|
name={breadcrumb}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
<Dropdown
|
||||||
|
startIndex={breadcrumbs.prefixBreadcrumbs.length}
|
||||||
|
breadcrumbs={breadcrumbs.collapsedBreadcrumbs}
|
||||||
|
onBreadcrumbClick={handleClickBreadcrumb}
|
||||||
|
/>
|
||||||
|
<BreadcrumbItem
|
||||||
|
index={prefix.length - 1}
|
||||||
|
handleClick={handleClickBreadcrumb}
|
||||||
|
name={breadcrumbs.lastBreadcrumb}
|
||||||
|
isActive={true}
|
||||||
|
disabled={true}
|
||||||
|
showSeparator={false}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -60,7 +60,7 @@ const OnlineDrive = ({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
onDataSourceNodeCompleted: (documentsData: DataSourceNodeCompletedResponse) => {
|
onDataSourceNodeCompleted: (documentsData: DataSourceNodeCompletedResponse) => {
|
||||||
const newFileList = convertOnlineDriveDataToFileList(documentsData.data)
|
const newFileList = convertOnlineDriveDataToFileList(documentsData.data, prefix)
|
||||||
setFileList([...fileList, ...newFileList])
|
setFileList([...fileList, ...newFileList])
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
},
|
},
|
||||||
|
|||||||
@ -7,14 +7,18 @@ export const isFile = (path: string): boolean => {
|
|||||||
return filePathRegex.test(path)
|
return filePathRegex.test(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const hasBuckets = (data: OnlineDriveData[]): boolean => {
|
export const isBucketListInitiation = (data: OnlineDriveData[], prefix: string[]): boolean => {
|
||||||
|
if (prefix.length > 0) return false
|
||||||
return data.length > 1 || (data.length === 1 && data[0].files.length === 0)
|
return data.length > 1 || (data.length === 1 && data[0].files.length === 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const convertOnlineDriveDataToFileList = (data: OnlineDriveData[]): OnlineDriveFile[] => {
|
export const convertOnlineDriveDataToFileList = (data: OnlineDriveData[], prefix: string[]): OnlineDriveFile[] => {
|
||||||
const fileList: OnlineDriveFile[] = []
|
const fileList: OnlineDriveFile[] = []
|
||||||
|
|
||||||
if (hasBuckets(data)) {
|
if (data.length === 0)
|
||||||
|
return fileList
|
||||||
|
|
||||||
|
if (isBucketListInitiation(data, prefix)) {
|
||||||
data.forEach((item) => {
|
data.forEach((item) => {
|
||||||
fileList.push({
|
fileList.push({
|
||||||
key: item.bucket,
|
key: item.bucket,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user