feat: Enhance Online Drive component with bucket handling and breadcrumb navigation

This commit is contained in:
twwu 2025-08-13 11:34:22 +08:00
parent 71d8a0c0b6
commit cf46fba753
7 changed files with 74 additions and 9 deletions

View File

@ -0,0 +1,35 @@
import React from 'react'
import cn from '@/utils/classnames'
import { useTranslation } from 'react-i18next'
type DriveProps = {
prefix: string[]
handleBackToRoot: () => void
}
const Drive = ({
prefix,
handleBackToRoot,
}: DriveProps) => {
const { t } = useTranslation()
return (
<>
<button
type='button'
className={cn(
'max-w-full shrink truncate rounded-md px-[5px] py-1',
prefix.length > 0 && 'system-sm-medium text-text-secondary hover:bg-state-base-hover',
prefix.length === 0 && 'system-sm-regular text-text-tertiary',
)}
onClick={handleBackToRoot}
disabled={prefix.length === 0}
>
{t('datasetPipeline.onlineDrive.breadcrumbs.allFiles')}
</button>
{prefix.length > 0 && <span className='system-xs-regular text-divider-deep'>/</span>}
</>
)
}
export default React.memo(Drive)

View File

@ -1,9 +1,10 @@
import React, { useCallback, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import { useDataSourceStore } from '../../../../store'
import { useDataSourceStore, useDataSourceStoreWithSelector } from '../../../../store'
import Bucket from './bucket'
import BreadcrumbItem from './item'
import Dropdown from './dropdown'
import Drive from './drive'
type BreadcrumbsProps = {
prefix: string[]
@ -22,8 +23,9 @@ const Breadcrumbs = ({
}: BreadcrumbsProps) => {
const { t } = useTranslation()
const dataSourceStore = useDataSourceStore()
const hasBucket = useDataSourceStoreWithSelector(s => s.hasBucket)
const showSearchResult = !!keywords && searchResultsLength > 0
const isRoot = prefix.length === 0 && bucket === ''
const showBucketListTitle = prefix.length === 0 && hasBucket && bucket === ''
const displayBreadcrumbNum = useMemo(() => {
const num = isInPipeline ? 2 : 3
@ -57,6 +59,13 @@ const Breadcrumbs = ({
setPrefix([])
}, [dataSourceStore])
const handleBackToRoot = useCallback(() => {
const { setFileList, setSelectedFileIds, setPrefix } = dataSourceStore.getState()
setFileList([])
setSelectedFileIds([])
setPrefix([])
}, [dataSourceStore])
const handleClickBreadcrumb = useCallback((index: number) => {
const { prefix, setFileList, setSelectedFileIds, setPrefix } = dataSourceStore.getState()
const newPrefix = prefix.slice(0, index + 1)
@ -75,14 +84,14 @@ const Breadcrumbs = ({
})}
</div>
)}
{!showSearchResult && isRoot && (
{!showSearchResult && showBucketListTitle && (
<div className='system-sm-medium text-test-secondary px-[5px]'>
{t('datasetPipeline.onlineDrive.breadcrumbs.allBuckets')}
</div>
)}
{!showSearchResult && !isRoot && (
{!showSearchResult && !showBucketListTitle && (
<div className='flex w-full items-center gap-x-0.5 overflow-hidden'>
{bucket && (
{hasBucket && bucket && (
<Bucket
bucketName={bucket}
handleBackToBucketList={handleBackToBucketList}
@ -92,6 +101,12 @@ const Breadcrumbs = ({
showSeparator={prefix.length > 0}
/>
)}
{!hasBucket && (
<Drive
prefix={prefix}
handleBackToRoot={handleBackToRoot}
/>
)}
{!breadcrumbs.needCollapsed && (
<>
{breadcrumbs.original.map((breadcrumb, index) => {

View File

@ -80,15 +80,17 @@ const OnlineDrive = ({
},
{
onDataSourceNodeCompleted: (documentsData: DataSourceNodeCompletedResponse) => {
const { setFileList, isTruncated, currentNextPageParametersRef } = dataSourceStore.getState()
const { setFileList, isTruncated, currentNextPageParametersRef, setHasBucket } = dataSourceStore.getState()
const {
fileList: newFileList,
isTruncated: newIsTruncated,
nextPageParameters: newNextPageParameters,
hasBucket: newHasBucket,
} = convertOnlineDriveData(documentsData.data, prefix, bucket)
setFileList([...fileList, ...newFileList])
isTruncated.current = newIsTruncated
currentNextPageParametersRef.current = newNextPageParameters
setHasBucket(newHasBucket)
setIsLoading(false)
},
onDataSourceNodeError: (error: DataSourceNodeErrorResponse) => {

View File

@ -7,20 +7,23 @@ export const isFile = (type: 'file' | 'folder'): boolean => {
export const isBucketListInitiation = (data: OnlineDriveData[], prefix: string[], bucket: string): boolean => {
if (bucket || prefix.length > 0) return false
return data.length > 1 || (data.length === 1 && !!data[0].bucket && data[0].files.length === 0)
const hasBucket = data.every(item => !!item.bucket)
return hasBucket && (data.length > 1 || (data.length === 1 && !!data[0].bucket && data[0].files.length === 0))
}
export const convertOnlineDriveData = (data: OnlineDriveData[], prefix: string[], bucket: string): {
fileList: OnlineDriveFile[],
isTruncated: boolean,
nextPageParameters: Record<string, any>
hasBucket: boolean
} => {
const fileList: OnlineDriveFile[] = []
let isTruncated = false
let nextPageParameters: Record<string, any> = {}
let hasBucket = false
if (data.length === 0)
return { fileList, isTruncated, nextPageParameters }
return { fileList, isTruncated, nextPageParameters, hasBucket }
if (isBucketListInitiation(data, prefix, bucket)) {
data.forEach((item) => {
@ -30,6 +33,7 @@ export const convertOnlineDriveData = (data: OnlineDriveData[], prefix: string[]
type: OnlineDriveFileType.bucket,
})
})
hasBucket = true
}
else {
data[0].files.forEach((file) => {
@ -44,6 +48,7 @@ export const convertOnlineDriveData = (data: OnlineDriveData[], prefix: string[]
})
isTruncated = data[0].is_truncated ?? false
nextPageParameters = data[0].next_page_parameters ?? {}
hasBucket = !!data[0].bucket
}
return { fileList, isTruncated, nextPageParameters }
return { fileList, isTruncated, nextPageParameters, hasBucket }
}

View File

@ -17,6 +17,8 @@ export type OnlineDriveSliceShape = {
setNextPageParameters: (nextPageParameters: Record<string, any>) => void
isTruncated: React.RefObject<boolean>
previewOnlineDriveFileRef: React.RefObject<OnlineDriveFile | undefined>
hasBucket: boolean
setHasBucket: (hasBucket: boolean) => void
}
export const createOnlineDriveSlice: StateCreator<OnlineDriveSliceShape> = (set, get) => {
@ -53,5 +55,9 @@ export const createOnlineDriveSlice: StateCreator<OnlineDriveSliceShape> = (set,
})),
isTruncated: { current: false },
previewOnlineDriveFileRef: { current: undefined },
hasBucket: false,
setHasBucket: (hasBucket: boolean) => set(() => ({
hasBucket,
})),
})
}

View File

@ -122,6 +122,7 @@ const translation = {
notConnectedTip: 'To sync with {{name}}, connection to {{name}} must be established first.',
breadcrumbs: {
allBuckets: 'All Cloud Storage Buckets',
allFiles: 'All Files',
searchResult: 'Find {{searchResultsLength}} items in "{{folderName}}" folder',
searchPlaceholder: 'Search files...',
},

View File

@ -122,6 +122,7 @@ const translation = {
notConnectedTip: '同步 {{name}} 内容前, 须先绑定 {{name}}。',
breadcrumbs: {
allBuckets: '所有云存储桶',
allFiles: '所有文件',
searchResult: '在 "{{folderName}}" 文件夹中找到 {{searchResultsLength}} 个项目',
searchPlaceholder: '搜索文件...',
},