123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196 |
- import { type FC, useMemo, useState } from 'react'
- import { RiArrowDownSLine, RiArrowRightSLine } from '@remixicon/react'
- import { useTranslation } from 'react-i18next'
- import { EditSlice } from '../../../formatted-text/flavours/edit-slice'
- import { useDocumentContext } from '../index'
- import { FormattedText } from '../../../formatted-text/formatted'
- import Empty from './common/empty'
- import FullDocListSkeleton from './skeleton/full-doc-list-skeleton'
- import { useSegmentListContext } from './index'
- 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'
- type IChildSegmentCardProps = {
- childChunks: ChildChunkDetail[]
- parentChunkId: string
- handleInputChange?: (value: string) => void
- handleAddNewChildChunk?: (parentChunkId: string) => void
- enabled: boolean
- onDelete?: (segId: string, childChunkId: string) => Promise<void>
- onClickSlice?: (childChunk: ChildChunkDetail) => void
- total?: number
- inputValue?: string
- onClearFilter?: () => void
- isLoading?: boolean
- focused?: boolean
- }
- const ChildSegmentList: FC<IChildSegmentCardProps> = ({
- childChunks,
- parentChunkId,
- handleInputChange,
- handleAddNewChildChunk,
- enabled,
- onDelete,
- onClickSlice,
- total,
- inputValue,
- onClearFilter,
- isLoading,
- focused = false,
- }) => {
- const { t } = useTranslation()
- const parentMode = useDocumentContext(s => s.parentMode)
- const currChildChunk = useSegmentListContext(s => s.currChildChunk)
- const [collapsed, setCollapsed] = useState(true)
- const toggleCollapse = () => {
- setCollapsed(!collapsed)
- }
- const isParagraphMode = useMemo(() => {
- return parentMode === 'paragraph'
- }, [parentMode])
- const isFullDocMode = useMemo(() => {
- return parentMode === 'full-doc'
- }, [parentMode])
- const contentOpacity = useMemo(() => {
- return (enabled || focused) ? '' : 'opacity-50 group-hover/card:opacity-100'
- }, [enabled, focused])
- const totalText = useMemo(() => {
- const isSearch = inputValue !== '' && isFullDocMode
- if (!isSearch) {
- const text = isFullDocMode
- ? !total
- ? '--'
- : formatNumber(total)
- : formatNumber(childChunks.length)
- const count = isFullDocMode
- ? text === '--'
- ? 0
- : total
- : childChunks.length
- return `${text} ${t('datasetDocuments.segment.childChunks', { count })}`
- }
- else {
- const text = !total ? '--' : formatNumber(total)
- const count = text === '--' ? 0 : total
- return `${count} ${t('datasetDocuments.segment.searchResults', { count })}`
- }
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [isFullDocMode, total, childChunks.length, inputValue])
- return (
- <div className={classNames(
- 'flex flex-col',
- contentOpacity,
- isParagraphMode ? 'pt-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-background-default' : '')}>
- <div className={classNames(
- 'h-7 flex items-center pl-1 pr-3 rounded-lg',
- isParagraphMode && 'cursor-pointer',
- (isParagraphMode && collapsed) && 'bg-dataset-child-chunk-expand-btn-bg',
- isFullDocMode && 'pl-0',
- )}
- onClick={(event) => {
- event.stopPropagation()
- toggleCollapse()
- }}
- >
- {
- isParagraphMode
- ? collapsed
- ? (
- <RiArrowRightSLine className='w-4 h-4 text-text-secondary opacity-50 mr-0.5' />
- )
- : (<RiArrowDownSLine className='w-4 h-4 text-text-secondary mr-0.5' />)
- : null
- }
- <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
- type='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' : '',
- (isFullDocMode && isLoading) ? 'text-components-button-secondary-accent-text-disabled' : '',
- )}
- onClick={(event) => {
- event.stopPropagation()
- handleAddNewChildChunk?.(parentChunkId)
- }}
- disabled={isLoading}
- >
- {t('common.operation.add')}
- </button>
- </div>
- {isFullDocMode
- ? <Input
- showLeftIcon
- showClearIcon
- wrapperClassName='!w-52'
- value={inputValue}
- onChange={e => handleInputChange?.(e.target.value)}
- onClear={() => handleInputChange?.('')}
- />
- : null}
- </div>
- {isLoading ? <FullDocListSkeleton /> : null}
- {((isFullDocMode && !isLoading) || !collapsed)
- ? <div className={classNames('flex gap-x-0.5', isFullDocMode ? 'grow mb-6' : 'items-center')}>
- {isParagraphMode && (
- <div className='self-stretch'>
- <Divider type='vertical' className='w-[2px] mx-[7px] bg-text-accent-secondary' />
- </div>
- )}
- {childChunks.length > 0
- ? <FormattedText className={classNames('w-full !leading-6 flex flex-col', isParagraphMode ? 'gap-y-2' : 'gap-y-3')}>
- {childChunks.map((childChunk) => {
- const edited = childChunk.updated_at !== childChunk.created_at
- const focused = currChildChunk?.childChunkInfo?.id === childChunk.id
- return <EditSlice
- key={childChunk.id}
- label={`C-${childChunk.position}${edited ? ` · ${t('datasetDocuments.segment.edited')}` : ''}`}
- text={childChunk.content}
- onDelete={() => onDelete?.(childChunk.segment_id, childChunk.id)}
- labelClassName={focused ? 'bg-state-accent-solid text-text-primary-on-surface' : ''}
- labelInnerClassName={'text-[10px] font-semibold align-bottom leading-6'}
- contentClassName={classNames('!leading-6', focused ? 'bg-state-accent-hover-alt text-text-primary' : '')}
- showDivider={false}
- onClick={(e) => {
- e.stopPropagation()
- onClickSlice?.(childChunk)
- }}
- offsetOptions={({ rects }) => {
- return {
- mainAxis: isFullDocMode ? -rects.floating.width : 12 - rects.floating.width,
- crossAxis: (20 - rects.floating.height) / 2,
- }
- }}
- />
- })}
- </FormattedText>
- : inputValue !== ''
- ? <div className='h-full w-full'>
- <Empty onClearFilter={onClearFilter!} />
- </div>
- : null
- }
- </div>
- : null}
- </div>
- )
- }
- export default ChildSegmentList
|