feat: update background styles and add loading skeletons for document detail components

This commit is contained in:
twwu 2024-12-16 17:10:33 +08:00
parent 340a052d44
commit faa5b63ed4
14 changed files with 296 additions and 51 deletions

View File

@ -30,9 +30,12 @@ export const SkeletonRectangle: FC<SkeletonProps> = (props) => {
)
}
export const SkeletonPoint: FC = () =>
<div className='text-text-quaternary text-xs font-medium'>·</div>
export const SkeletonPoint: FC<SkeletonProps> = (props) => {
const { className, ...rest } = props
return (
<div className={classNames('text-text-quaternary text-xs font-medium', className)} {...rest}>·</div>
)
}
/** Usage
* <SkeletonContanier>
* <SkeletonRow>

View File

@ -4,12 +4,12 @@ import { useTranslation } from 'react-i18next'
import { EditSlice } from '../../../formatted-text/flavours/edit-slice'
import { useDocumentContext } from '../index'
import Empty from './common/empty'
import FullDocListSkeleton from './skeleton/full-doc-list-skeleton'
import type { ChildChunkDetail } from '@/models/datasets'
import Input from '@/app/components/base/input'
import classNames from '@/utils/classnames'
import Divider from '@/app/components/base/divider'
import { formatNumber } from '@/utils/format'
import Loading from '@/app/components/base/loading'
type IChildSegmentCardProps = {
childChunks: ChildChunkDetail[]
@ -72,7 +72,7 @@ const ChildSegmentList: FC<IChildSegmentCardProps> = ({
? 0
: total
: childChunks.length
return `${isFullDocMode ? count : childChunks.length} ${t('datasetDocuments.segment.childChunks', { count })}`
return `${text} ${t('datasetDocuments.segment.childChunks', { count })}`
}
else {
const text = !total ? '--' : formatNumber(total)
@ -83,10 +83,15 @@ const ChildSegmentList: FC<IChildSegmentCardProps> = ({
}, [isFullDocMode, total, childChunks.length, inputValue])
return (
<div className={classNames('flex flex-col', contentOpacity, isParagraphMode ? 'p-1 pb-2' : 'px-3 grow')}>
<div className={classNames(
'flex flex-col',
contentOpacity,
isParagraphMode ? 'p-1 pb-2' : 'px-3 grow',
(isFullDocMode && isLoading) && 'overflow-y-hidden',
)}>
{isFullDocMode ? <Divider type='horizontal' className='h-[1px] bg-divider-subtle my-1' /> : null}
<div className={classNames('flex items-center justify-between', isFullDocMode ? 'pt-2 pb-3 sticky -top-2 left-0 bg-components-panel-bg' : '')}>
<div className={classNames('h-7 flex items-center pl-1 pr-3 rounded-lg', (isParagraphMode && collapsed) ? 'bg-dataset-child-chunk-expand-btn-bg' : '')} onClick={(event) => {
<div className={classNames('h-7 flex items-center pl-1 pr-3 rounded-lg', (isParagraphMode && collapsed) && 'bg-dataset-child-chunk-expand-btn-bg', isFullDocMode && 'pl-0')} onClick={(event) => {
event.stopPropagation()
toggleCollapse()
}}>
@ -102,11 +107,16 @@ const ChildSegmentList: FC<IChildSegmentCardProps> = ({
<span className='text-text-secondary system-sm-semibold-uppercase'>{totalText}</span>
<span className={classNames('text-text-quaternary text-xs font-medium pl-1.5', isParagraphMode ? 'hidden group-hover/card:inline-block' : '')}>·</span>
<button
className={classNames('px-1.5 py-1 text-components-button-secondary-accent-text system-xs-semibold-uppercase', isParagraphMode ? 'hidden group-hover/card:inline-block' : '')}
className={classNames(
'px-1.5 py-1 text-components-button-secondary-accent-text system-xs-semibold-uppercase',
isParagraphMode ? 'hidden group-hover/card:inline-block' : '',
(isFullDocMode && isLoading) ? 'text-components-button-secondary-accent-text-disabled' : '',
)}
onClick={(event) => {
event.stopPropagation()
handleAddNewChildChunk?.(parentChunkId)
}}
disabled={isLoading}
>
{t('common.operation.add')}
</button>
@ -122,10 +132,10 @@ const ChildSegmentList: FC<IChildSegmentCardProps> = ({
/>
: null}
</div>
{(isFullDocMode || !collapsed)
? <div className={classNames('flex gap-x-0.5', isFullDocMode ? 'grow' : '')}>
{isLoading ? <FullDocListSkeleton /> : null}
{((isFullDocMode && !isLoading) || !collapsed)
? <div className={classNames('flex items-center gap-x-0.5', isFullDocMode ? 'grow' : '')}>
{isParagraphMode && <Divider type='vertical' className='h-auto w-[2px] mx-[7px] bg-text-accent-secondary' />}
{isLoading ? <Loading type='app' /> : null}
{childChunks.length > 0
? <div className={classNames('w-full !leading-5 flex flex-col', isParagraphMode ? 'gap-y-2' : 'gap-y-3')}>
{childChunks.map((childChunk) => {

View File

@ -69,7 +69,7 @@ const Empty: FC<IEmptyProps> = ({
))
}
</div>
<div className='h-full w-full absolute top-0 left-0 bg-dataset-chunk-list-empty-bg -z-10' />
<div className='h-full w-full absolute top-0 left-0 bg-dataset-chunk-list-mask-bg -z-10' />
</div>
)
}

View File

@ -507,6 +507,7 @@ const Completed: FC<ICompletedProps> = ({
checked={isAllSelected}
mixed={!isAllSelected && isSomeSelected}
onCheck={onSelectedAll}
disabled={isLoadingSegmentList}
/>
<div className={cn('system-sm-semibold-uppercase pl-5', s.totalText)}>{totalText}</div>
<SimpleSelect
@ -533,11 +534,14 @@ const Completed: FC<ICompletedProps> = ({
{/* Segment list */}
{
isFullDocMode
? <div className='flex flex-col grow relative overflow-x-hidden overflow-y-auto'>
? <div className={cn(
'flex flex-col grow relative overflow-x-hidden',
(isLoadingSegmentList || isLoadingChildSegmentList) ? 'overflow-y-hidden' : 'overflow-y-auto',
)}>
<SegmentCard
detail={segments[0]}
onClick={() => onClickCard(segments[0])}
loading={false}
loading={isLoadingSegmentList}
/>
<ChildSegmentList
parentChunkId={segments[0]?.id}
@ -550,7 +554,7 @@ const Completed: FC<ICompletedProps> = ({
total={childChunkListData?.total || 0}
inputValue={inputValue}
onClearFilter={onClearFilter}
isLoading={isLoadingChildSegmentList}
isLoading={isLoadingSegmentList || isLoadingChildSegmentList}
/>
</div>
: <SegmentList

View File

@ -7,6 +7,7 @@ import ChildSegmentList from './child-segment-list'
import Tag from './common/tag'
import Dot from './common/dot'
import { SegmentIndexTag } from './common/segment-index-tag'
import ParentChunkCardSkeleton from './skeleton/parent-chunk-card-skeleton'
import { useSegmentListContext } from './index'
import type { ChildChunkDetail, SegmentDetailModel } from '@/models/datasets'
import Indicator from '@/app/components/header/indicator'
@ -122,6 +123,9 @@ const SegmentCard: FC<ISegmentCardProps> = ({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isParentChildMode])
if (loading)
return <ParentChunkCardSkeleton />
return (
<div
className={cn('w-full px-3 rounded-xl group/card', isFullDocMode ? '' : 'pt-2.5 pb-2 hover:bg-dataset-chunk-detail-card-hover-bg', className)}
@ -208,30 +212,25 @@ const SegmentCard: FC<ISegmentCardProps> = ({
: null}
</>
</div>
{loading
? (
<div className=''>
<div className='' />
</div>
)
: (
<>
<div className={cn('text-text-secondary body-md-regular -tracking-[0.07px] mt-0.5',
textOpacity,
isFullDocMode ? 'line-clamp-3' : isCollapsed ? 'line-clamp-2' : 'line-clamp-20',
)}>
{renderContent()}
</div>
{isGeneralMode && <div className={cn('flex items-center gap-x-2 py-1.5', textOpacity)}>
{keywords?.map(keyword => <Tag key={keyword} text={keyword} />)}
</div>}
{
isFullDocMode
? <button className='mt-0.5 mb-2 text-text-accent system-xs-semibold-uppercase' onClick={() => onClick?.()}>{t('common.operation.viewMore')}</button>
: null
}
{
child_chunks.length > 0
<div className={cn('text-text-secondary body-md-regular -tracking-[0.07px] mt-0.5',
textOpacity,
isFullDocMode ? 'line-clamp-3' : isCollapsed ? 'line-clamp-2' : 'line-clamp-20',
)}>
{renderContent()}
</div>
{isGeneralMode && <div className={cn('flex items-center gap-x-2 py-1.5', textOpacity)}>
{keywords?.map(keyword => <Tag key={keyword} text={keyword} />)}
</div>}
{
isFullDocMode
? <button
className='mt-0.5 mb-2 text-text-accent system-xs-semibold-uppercase'
onClick={() => onClick?.()}
>{t('common.operation.viewMore')}</button>
: null
}
{
child_chunks.length > 0
&& <ChildSegmentList
parentChunkId={id}
childChunks={child_chunks}
@ -240,9 +239,6 @@ const SegmentCard: FC<ISegmentCardProps> = ({
handleAddNewChildChunk={handleAddNewChildChunk}
onClickSlice={onClickSlice}
/>
}
</>
)
}
{showModal
&& <Confirm

View File

@ -1,11 +1,12 @@
import React, { type ForwardedRef } from 'react'
import React, { type ForwardedRef, useMemo } from 'react'
import { useDocumentContext } from '../index'
import SegmentCard from './segment-card'
import Empty from './common/empty'
import GeneralListSkeleton from './skeleton/general-list-skeleton'
import ParagraphListSkeleton from './skeleton/paragraph-list-skeleton'
import type { ChildChunkDetail, SegmentDetailModel } from '@/models/datasets'
import Checkbox from '@/app/components/base/checkbox'
import Loading from '@/app/components/base/loading'
import Divider from '@/app/components/base/divider'
import classNames from '@/utils/classnames'
type ISegmentListProps = {
isLoading: boolean
@ -40,8 +41,16 @@ const SegmentList = React.forwardRef(({
}: ISegmentListProps,
ref: ForwardedRef<HTMLDivElement>,
) => {
const [mode, parentMode] = useDocumentContext(s => [s.mode, s.parentMode])
const Skeleton = useMemo(() => {
return (mode === 'hierarchical' && parentMode === 'paragraph') ? ParagraphListSkeleton : GeneralListSkeleton
}, [mode, parentMode])
// Loading skeleton
if (isLoading)
return <Loading type='app' />
return <Skeleton />
// Search result is empty
if (items.length === 0) {
return (
<div className='h-full pl-6'>
@ -50,7 +59,7 @@ ref: ForwardedRef<HTMLDivElement>,
)
}
return (
<div ref={ref} className={classNames('flex flex-col h-full overflow-y-auto')}>
<div ref={ref} className={'flex flex-col grow overflow-y-auto'}>
{
items.map((segItem) => {
const isLast = items[items.length - 1].id === segItem.id

View File

@ -0,0 +1,25 @@
import React from 'react'
const Slice = React.memo(() => {
return (
<div className='flex flex-col gap-y-1'>
<div className='w-full h-5 bg-state-base-hover flex items-center'>
<span className='w-[30px] h-5 bg-state-base-hover-alt' />
</div>
<div className='w-2/3 h-5 bg-state-base-hover' />
</div>
)
})
Slice.displayName = 'Slice'
const FullDocListSkeleton = () => {
return (
<div className='w-full grow flex flex-col gap-y-3 relative z-10 overflow-y-hidden'>
<div className='absolute top-0 left-0 bottom-14 w-full h-full bg-dataset-chunk-list-mask-bg z-20' />
{[...Array(15)].map((_, index) => <Slice key={index} />)}
</div>
)
}
export default React.memo(FullDocListSkeleton)

View File

@ -0,0 +1,74 @@
import React from 'react'
import {
SkeletonContanier,
SkeletonPoint,
SkeletonRectangle,
SkeletonRow,
} from '@/app/components/base/skeleton'
import Checkbox from '@/app/components/base/checkbox'
import Divider from '@/app/components/base/divider'
const CardSkelton = React.memo(() => {
return (
<SkeletonContanier className='p-1 pb-2 gap-y-0'>
<SkeletonContanier className='px-2 pt-1.5 gap-y-0.5'>
<SkeletonRow className='py-0.5'>
<SkeletonRectangle className='w-[72px] bg-text-quaternary' />
<SkeletonPoint className='opacity-20' />
<SkeletonRectangle className='w-24 bg-text-quaternary' />
<SkeletonPoint className='opacity-20' />
<SkeletonRectangle className='w-24 bg-text-quaternary' />
<SkeletonRow className='grow justify-end gap-1'>
<SkeletonRectangle className='w-12 bg-text-quaternary' />
<SkeletonRectangle className='w-2 bg-text-quaternary mx-1' />
</SkeletonRow>
</SkeletonRow>
<SkeletonRow className='py-0.5'>
<SkeletonRectangle className='w-full bg-text-quaternary' />
</SkeletonRow>
<SkeletonRow className='py-0.5'>
<SkeletonRectangle className='w-full bg-text-quaternary' />
</SkeletonRow>
<SkeletonRow className='py-0.5'>
<SkeletonRectangle className='w-2/3 bg-text-quaternary' />
</SkeletonRow>
</SkeletonContanier>
<SkeletonContanier className='px-2 py-1.5'>
<SkeletonRow>
<SkeletonRectangle className='w-14 bg-text-quaternary' />
<SkeletonRectangle className='w-[88px] bg-text-quaternary' />
<SkeletonRectangle className='w-14 bg-text-quaternary' />
</SkeletonRow>
</SkeletonContanier>
</SkeletonContanier>
)
})
CardSkelton.displayName = 'CardSkelton'
const GeneralListSkeleton = () => {
return (
<div className='relative flex flex-col grow overflow-y-hidden z-10'>
<div className='absolute top-0 left-0 w-full h-full bg-dataset-chunk-list-mask-bg z-20' />
{[...Array(10)].map((_, index) => {
return (
<div key={index} className='flex items-start gap-x-2'>
<Checkbox
key={`${index}-checkbox`}
className='shrink-0 mt-3.5'
disabled
/>
<div className='grow'>
<CardSkelton />
{index !== 9 && <div className='w-full px-3'>
<Divider type='horizontal' className='bg-divider-subtle my-1' />
</div>}
</div>
</div>
)
})}
</div>
)
}
export default React.memo(GeneralListSkeleton)

View File

@ -0,0 +1,76 @@
import React from 'react'
import { RiArrowRightSLine } from '@remixicon/react'
import {
SkeletonContanier,
SkeletonPoint,
SkeletonRectangle,
SkeletonRow,
} from '@/app/components/base/skeleton'
import Checkbox from '@/app/components/base/checkbox'
import Divider from '@/app/components/base/divider'
const CardSkelton = React.memo(() => {
return (
<SkeletonContanier className='p-1 pb-2 gap-y-0'>
<SkeletonContanier className='px-2 pt-1.5 gap-y-0.5'>
<SkeletonRow className='py-0.5'>
<SkeletonRectangle className='w-[72px] bg-text-quaternary' />
<SkeletonPoint className='opacity-20' />
<SkeletonRectangle className='w-24 bg-text-quaternary' />
<SkeletonPoint className='opacity-20' />
<SkeletonRectangle className='w-24 bg-text-quaternary' />
<SkeletonRow className='grow justify-end gap-1'>
<SkeletonRectangle className='w-12 bg-text-quaternary' />
<SkeletonRectangle className='w-2 bg-text-quaternary mx-1' />
</SkeletonRow>
</SkeletonRow>
<SkeletonRow className='py-0.5'>
<SkeletonRectangle className='w-full bg-text-quaternary' />
</SkeletonRow>
<SkeletonRow className='py-0.5'>
<SkeletonRectangle className='w-full bg-text-quaternary' />
</SkeletonRow>
<SkeletonRow className='py-0.5'>
<SkeletonRectangle className='w-2/3 bg-text-quaternary' />
</SkeletonRow>
</SkeletonContanier>
<SkeletonContanier className='p-1 pb-2'>
<SkeletonRow>
<SkeletonRow className='h-7 pl-1 pr-3 gap-x-0.5 rounded-lg bg-dataset-child-chunk-expand-btn-bg'>
<RiArrowRightSLine className='w-4 h-4 text-text-secondary opacity-20' />
<SkeletonRectangle className='w-32 bg-text-quaternary' />
</SkeletonRow>
</SkeletonRow>
</SkeletonContanier>
</SkeletonContanier>
)
})
CardSkelton.displayName = 'CardSkelton'
const ParagraphListSkeleton = () => {
return (
<div className='relative flex flex-col h-full overflow-y-hidden z-10'>
<div className='absolute top-0 left-0 w-full h-full bg-dataset-chunk-list-mask-bg z-20' />
{[...Array(10)].map((_, index) => {
return (
<div key={index} className='flex items-start gap-x-2'>
<Checkbox
key={`${index}-checkbox`}
className='shrink-0 mt-3.5'
disabled
/>
<div className='grow'>
<CardSkelton />
{index !== 9 && <div className='w-full px-3'>
<Divider type='horizontal' className='bg-divider-subtle my-1' />
</div>}
</div>
</div>
)
})}
</div>
)
}
export default React.memo(ParagraphListSkeleton)

View File

@ -0,0 +1,45 @@
import React from 'react'
import { useTranslation } from 'react-i18next'
import {
SkeletonContanier,
SkeletonPoint,
SkeletonRectangle,
SkeletonRow,
} from '@/app/components/base/skeleton'
const ParentChunkCardSkelton = () => {
const { t } = useTranslation()
return (
<div className='flex flex-col pb-2'>
<SkeletonContanier className='p-1 pb-0 gap-y-0'>
<SkeletonContanier className='px-2 pt-1.5 gap-y-0.5'>
<SkeletonRow className='py-0.5'>
<SkeletonRectangle className='w-[72px] bg-text-quaternary' />
<SkeletonPoint className='opacity-20' />
<SkeletonRectangle className='w-24 bg-text-quaternary' />
<SkeletonPoint className='opacity-20' />
<SkeletonRectangle className='w-24 bg-text-quaternary' />
</SkeletonRow>
<SkeletonRow className='py-0.5'>
<SkeletonRectangle className='w-full bg-text-quaternary' />
</SkeletonRow>
<SkeletonRow className='py-0.5'>
<SkeletonRectangle className='w-full bg-text-quaternary' />
</SkeletonRow>
<SkeletonRow className='py-0.5'>
<SkeletonRectangle className='w-2/3 bg-text-quaternary' />
</SkeletonRow>
</SkeletonContanier>
</SkeletonContanier>
<div className='flex items-center px-3 mt-0.5'>
<button className='pt-0.5 text-components-button-secondary-accent-text-disabled system-xs-semibold-uppercase' disabled>
{t('common.operation.viewMore')}
</button>
</div>
</div>
)
}
ParentChunkCardSkelton.displayName = 'ParentChunkCardSkelton'
export default React.memo(ParentChunkCardSkelton)

View File

@ -243,7 +243,10 @@ const DocumentDetail: FC<Props> = ({ datasetId, documentId }) => {
<div className='flex flex-row flex-1' style={{ height: 'calc(100% - 4rem)' }}>
{isDetailLoading
? <Loading type='app' />
: <div className={`h-full w-full flex flex-col ${embedding ? 'px-6 py-3 sm:py-12 sm:px-16' : `relative pt-3 ${isFullDocMode ? 'pl-11' : 'pl-5'} pr-11`}`}>
: <div className={cn('h-full w-full flex flex-col',
embedding ? 'px-6 py-3 sm:py-12 sm:px-16' : 'relative pt-3 pr-11',
isFullDocMode ? 'pl-11 pt-4' : 'pl-5',
)}>
{embedding
? <Embedding detail={documentDetail} detailUpdate={detailMutate} />
: <Completed

View File

@ -106,7 +106,7 @@ const config = {
'dataset-option-card-blue-gradient': 'var(--color-dataset-option-card-blue-gradient)',
'dataset-option-card-purple-gradient': 'var(--color-dataset-option-card-purple-gradient)',
'dataset-option-card-orange-gradient': 'var(--color-dataset-option-card-orange-gradient)',
'dataset-chunk-list-empty-bg': 'var(--color-dataset-chunk-list-empty-bg)',
'dataset-chunk-list-mask-bg': 'var(--color-dataset-chunk-list-mask-bg)',
},
lineClamp: {
20: '20',

View File

@ -9,5 +9,5 @@ html[data-theme="dark"] {
--color-dataset-option-card-blue-gradient: linear-gradient(90deg, #24252E 0%, #1E1E21 100%);
--color-dataset-option-card-purple-gradient: linear-gradient(90deg, #25242E 0%, #1E1E21 100%);
--color-dataset-option-card-orange-gradient: linear-gradient(90deg, #2B2322 0%, #1E1E21 100%);
--color-dataset-chunk-list-empty-bg: linear-gradient(180deg, rgba(34, 34, 37, 0.00) 0%, #222225 100%);
--color-dataset-chunk-list-mask-bg: linear-gradient(180deg, rgba(34, 34, 37, 0.00) 0%, #222225 100%);
}

View File

@ -9,5 +9,5 @@ html[data-theme="light"] {
--color-dataset-option-card-blue-gradient: linear-gradient(90deg, #F2F4F7 0%, #F9FAFB 100%);
--color-dataset-option-card-purple-gradient: linear-gradient(90deg, #F0EEFA 0%, #F9FAFB 100%);
--color-dataset-option-card-orange-gradient: linear-gradient(90deg, #F8F2EE 0%, #F9FAFB 100%);
--color-dataset-chunk-list-empty-bg: linear-gradient(180deg, rgba(255, 255, 255, 0.00) 0%, #FCFCFD 100%);
--color-dataset-chunk-list-mask-bg: linear-gradient(180deg, rgba(255, 255, 255, 0.00) 0%, #FCFCFD 100%);
}