mirror of
https://git.mirrors.martin98.com/https://github.com/langgenius/dify.git
synced 2025-08-15 02:06:01 +08:00
feat: add IndeterminateIcon component and update Checkbox to support indeterminate state
refactor: remove mixed state handling and update related styles fix: update useCallback dependencies for better performance
This commit is contained in:
parent
523efbfea5
commit
4d1a9d1a83
@ -0,0 +1,9 @@
|
||||
const IndeterminateIcon = () => {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12" fill="none">
|
||||
<path d="M2.5 6H9.5" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default IndeterminateIcon
|
@ -1,5 +0,0 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="check">
|
||||
<path id="Vector 1" d="M2.5 6H9.5" stroke="white" stroke-width="1.5" stroke-linecap="round"/>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 217 B |
@ -1,10 +0,0 @@
|
||||
.mixed {
|
||||
background: var(--color-components-checkbox-bg) url(./assets/mixed.svg) center center no-repeat;
|
||||
background-size: 12px 12px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.checked.disabled {
|
||||
background-color: #d0d5dd;
|
||||
border-color: #d0d5dd;
|
||||
}
|
@ -1,23 +1,25 @@
|
||||
import { RiCheckLine } from '@remixicon/react'
|
||||
import s from './index.module.css'
|
||||
import cn from '@/utils/classnames'
|
||||
import IndeterminateIcon from './assets/indeterminate-icon'
|
||||
|
||||
type CheckboxProps = {
|
||||
id?: string
|
||||
checked?: boolean
|
||||
onCheck?: () => void
|
||||
className?: string
|
||||
disabled?: boolean
|
||||
mixed?: boolean
|
||||
indeterminate?: boolean
|
||||
}
|
||||
|
||||
const Checkbox = ({ checked, onCheck, className, disabled, mixed }: CheckboxProps) => {
|
||||
const Checkbox = ({ id, checked, onCheck, className, disabled, indeterminate }: CheckboxProps) => {
|
||||
if (!checked) {
|
||||
return (
|
||||
<div
|
||||
id={id}
|
||||
className={cn(
|
||||
'h-4 w-4 cursor-pointer rounded-[4px] border border-components-checkbox-border bg-components-checkbox-bg-unchecked shadow-xs hover:border-components-checkbox-border-hover',
|
||||
mixed ? s.mixed : 'hover:bg-components-checkbox-bg-unchecked-hover',
|
||||
disabled && 'cursor-not-allowed border-components-checkbox-border-disabled bg-components-checkbox-bg-disabled hover:border-components-checkbox-border-disabled hover:bg-components-checkbox-bg-disabled',
|
||||
'flex h-4 w-4 cursor-pointer items-center justify-center rounded-[4px] border border-components-checkbox-border bg-components-checkbox-bg-unchecked shadow-xs',
|
||||
indeterminate ? 'border-none bg-components-checkbox-bg text-components-checkbox-icon' : 'hover:bg-components-checkbox-bg-unchecked-hover',
|
||||
disabled && 'cursor-not-allowed border-components-checkbox-border-disabled bg-components-checkbox-bg-disabled text-components-checkbox-icon-disabled hover:border-components-checkbox-border-disabled hover:bg-components-checkbox-bg-disabled',
|
||||
className,
|
||||
)}
|
||||
onClick={() => {
|
||||
@ -25,11 +27,16 @@ const Checkbox = ({ checked, onCheck, className, disabled, mixed }: CheckboxProp
|
||||
return
|
||||
onCheck?.()
|
||||
}}
|
||||
></div>
|
||||
>
|
||||
{indeterminate && (
|
||||
<IndeterminateIcon />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div
|
||||
id={id}
|
||||
className={cn(
|
||||
'flex h-4 w-4 cursor-pointer items-center justify-center rounded-[4px] bg-components-checkbox-bg text-components-checkbox-icon shadow-xs hover:bg-components-checkbox-bg-hover',
|
||||
disabled && 'cursor-not-allowed bg-components-checkbox-bg-disabled-checked text-components-checkbox-icon-disabled hover:bg-components-checkbox-bg-disabled-checked',
|
||||
@ -42,7 +49,7 @@ const Checkbox = ({ checked, onCheck, className, disabled, mixed }: CheckboxProp
|
||||
onCheck?.()
|
||||
}}
|
||||
>
|
||||
<RiCheckLine className={cn('h-3 w-3')} />
|
||||
<RiCheckLine className='h-3 w-3' />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -220,13 +220,11 @@ const Completed: FC<ICompletedProps> = ({
|
||||
const resetList = useCallback(() => {
|
||||
setSelectedSegmentIds([])
|
||||
invalidSegmentList()
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
}, [invalidSegmentList])
|
||||
|
||||
const resetChildList = useCallback(() => {
|
||||
invalidChildSegmentList()
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
}, [invalidChildSegmentList])
|
||||
|
||||
const onClickCard = (detail: SegmentDetailModel, isEditMode = false) => {
|
||||
setCurrSegment({ segInfo: detail, showModal: true, isEditMode })
|
||||
@ -253,7 +251,7 @@ const Completed: FC<ICompletedProps> = ({
|
||||
const invalidChunkListEnabled = useInvalid(useChunkListEnabledKey)
|
||||
const invalidChunkListDisabled = useInvalid(useChunkListDisabledKey)
|
||||
|
||||
const refreshChunkListWithStatusChanged = () => {
|
||||
const refreshChunkListWithStatusChanged = useCallback(() => {
|
||||
switch (selectedStatus) {
|
||||
case 'all':
|
||||
invalidChunkListDisabled()
|
||||
@ -262,7 +260,7 @@ const Completed: FC<ICompletedProps> = ({
|
||||
default:
|
||||
invalidSegmentList()
|
||||
}
|
||||
}
|
||||
}, [selectedStatus, invalidChunkListDisabled, invalidChunkListEnabled, invalidSegmentList])
|
||||
|
||||
const onChangeSwitch = useCallback(async (enable: boolean, segId?: string) => {
|
||||
const operationApi = enable ? enableSegment : disableSegment
|
||||
@ -280,8 +278,7 @@ const Completed: FC<ICompletedProps> = ({
|
||||
notify({ type: 'error', message: t('common.actionMsg.modifiedUnsuccessfully') })
|
||||
},
|
||||
})
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [datasetId, documentId, selectedSegmentIds, segments])
|
||||
}, [datasetId, documentId, selectedSegmentIds, segments, disableSegment, enableSegment, t, notify, refreshChunkListWithStatusChanged])
|
||||
|
||||
const { mutateAsync: deleteSegment } = useDeleteSegment()
|
||||
|
||||
@ -296,12 +293,11 @@ const Completed: FC<ICompletedProps> = ({
|
||||
notify({ type: 'error', message: t('common.actionMsg.modifiedUnsuccessfully') })
|
||||
},
|
||||
})
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [datasetId, documentId, selectedSegmentIds])
|
||||
}, [datasetId, documentId, selectedSegmentIds, deleteSegment, resetList, t, notify])
|
||||
|
||||
const { mutateAsync: updateSegment } = useUpdateSegment()
|
||||
|
||||
const refreshChunkListDataWithDetailChanged = () => {
|
||||
const refreshChunkListDataWithDetailChanged = useCallback(() => {
|
||||
switch (selectedStatus) {
|
||||
case 'all':
|
||||
invalidChunkListDisabled()
|
||||
@ -316,7 +312,7 @@ const Completed: FC<ICompletedProps> = ({
|
||||
invalidChunkListEnabled()
|
||||
break
|
||||
}
|
||||
}
|
||||
}, [selectedStatus, invalidChunkListDisabled, invalidChunkListEnabled, invalidChunkListAll])
|
||||
|
||||
const handleUpdateSegment = useCallback(async (
|
||||
segmentId: string,
|
||||
@ -375,17 +371,18 @@ const Completed: FC<ICompletedProps> = ({
|
||||
eventEmitter?.emit('update-segment-done')
|
||||
},
|
||||
})
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [segments, datasetId, documentId])
|
||||
}, [segments, datasetId, documentId, updateSegment, docForm, notify, eventEmitter, onCloseSegmentDetail, refreshChunkListDataWithDetailChanged, t])
|
||||
|
||||
useEffect(() => {
|
||||
resetList()
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [pathname])
|
||||
|
||||
useEffect(() => {
|
||||
if (importStatus === ProcessStatus.COMPLETED)
|
||||
resetList()
|
||||
}, [importStatus, resetList])
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [importStatus])
|
||||
|
||||
const onCancelBatchOperation = useCallback(() => {
|
||||
setSelectedSegmentIds([])
|
||||
@ -430,8 +427,7 @@ const Completed: FC<ICompletedProps> = ({
|
||||
const count = segmentListData?.total || 0
|
||||
return `${total} ${t('datasetDocuments.segment.searchResults', { count })}`
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [segmentListData?.total, mode, parentMode, searchValue, selectedStatus])
|
||||
}, [segmentListData, mode, parentMode, searchValue, selectedStatus, t])
|
||||
|
||||
const toggleFullScreen = useCallback(() => {
|
||||
setFullScreen(!fullScreen)
|
||||
@ -449,8 +445,7 @@ const Completed: FC<ICompletedProps> = ({
|
||||
resetList()
|
||||
currentPage !== totalPages && setCurrentPage(totalPages)
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [segmentListData, limit, currentPage])
|
||||
}, [segmentListData, limit, currentPage, resetList])
|
||||
|
||||
const { mutateAsync: deleteChildSegment } = useDeleteChildSegment()
|
||||
|
||||
@ -470,8 +465,7 @@ const Completed: FC<ICompletedProps> = ({
|
||||
},
|
||||
},
|
||||
)
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [datasetId, documentId, parentMode])
|
||||
}, [datasetId, documentId, parentMode, deleteChildSegment, resetList, resetChildList, t, notify])
|
||||
|
||||
const handleAddNewChildChunk = useCallback((parentChunkId: string) => {
|
||||
setShowNewChildSegmentModal(true)
|
||||
@ -490,8 +484,7 @@ const Completed: FC<ICompletedProps> = ({
|
||||
else {
|
||||
resetChildList()
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [parentMode, currChunkId, segments])
|
||||
}, [parentMode, currChunkId, segments, refreshChunkListDataWithDetailChanged, resetChildList])
|
||||
|
||||
const viewNewlyAddedChildChunk = useCallback(() => {
|
||||
const totalPages = childChunkListData?.total_pages || 0
|
||||
@ -505,8 +498,7 @@ const Completed: FC<ICompletedProps> = ({
|
||||
resetChildList()
|
||||
currentPage !== totalPages && setCurrentPage(totalPages)
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [childChunkListData, limit, currentPage])
|
||||
}, [childChunkListData, limit, currentPage, resetChildList])
|
||||
|
||||
const onClickSlice = useCallback((detail: ChildChunkDetail) => {
|
||||
setCurrChildChunk({ childChunkInfo: detail, showModal: true })
|
||||
@ -560,8 +552,7 @@ const Completed: FC<ICompletedProps> = ({
|
||||
eventEmitter?.emit('update-child-segment-done')
|
||||
},
|
||||
})
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [segments, childSegments, datasetId, documentId, parentMode])
|
||||
}, [segments, datasetId, documentId, parentMode, updateChildSegment, notify, eventEmitter, onCloseChildSegmentDetail, refreshChunkListDataWithDetailChanged, resetChildList, t])
|
||||
|
||||
const onClearFilter = useCallback(() => {
|
||||
setInputValue('')
|
||||
@ -570,6 +561,12 @@ const Completed: FC<ICompletedProps> = ({
|
||||
setCurrentPage(1)
|
||||
}, [])
|
||||
|
||||
const selectDefaultValue = useMemo(() => {
|
||||
if (selectedStatus === 'all')
|
||||
return 'all'
|
||||
return selectedStatus ? 1 : 0
|
||||
}, [selectedStatus])
|
||||
|
||||
return (
|
||||
<SegmentListContext.Provider value={{
|
||||
isCollapsed,
|
||||
@ -583,7 +580,7 @@ const Completed: FC<ICompletedProps> = ({
|
||||
<Checkbox
|
||||
className='shrink-0'
|
||||
checked={isAllSelected}
|
||||
mixed={!isAllSelected && isSomeSelected}
|
||||
indeterminate={!isAllSelected && isSomeSelected}
|
||||
onCheck={onSelectedAll}
|
||||
disabled={isLoadingSegmentList}
|
||||
/>
|
||||
@ -591,7 +588,7 @@ const Completed: FC<ICompletedProps> = ({
|
||||
<SimpleSelect
|
||||
onSelect={onChangeStatus}
|
||||
items={statusList.current}
|
||||
defaultValue={selectedStatus === 'all' ? 'all' : selectedStatus ? 1 : 0}
|
||||
defaultValue={selectDefaultValue}
|
||||
className={s.select}
|
||||
wrapperClassName='h-fit mr-2'
|
||||
optionWrapClassName='w-[160px]'
|
||||
|
@ -106,13 +106,11 @@ const SegmentCard: FC<ISegmentCardProps> = ({
|
||||
const wordCountText = useMemo(() => {
|
||||
const total = formatNumber(word_count)
|
||||
return `${total} ${t('datasetDocuments.segment.characters', { count: word_count })}`
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [word_count])
|
||||
}, [word_count, t])
|
||||
|
||||
const labelPrefix = useMemo(() => {
|
||||
return isParentChildMode ? t('datasetDocuments.segment.parentChunk') : t('datasetDocuments.segment.chunk')
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isParentChildMode])
|
||||
}, [isParentChildMode, t])
|
||||
|
||||
if (loading)
|
||||
return <ParentChunkCardSkeleton />
|
||||
|
@ -86,8 +86,7 @@ const SegmentDetail: FC<ISegmentDetailProps> = ({
|
||||
|
||||
const titleText = useMemo(() => {
|
||||
return isEditMode ? t('datasetDocuments.segment.editChunk') : t('datasetDocuments.segment.chunkDetail')
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isEditMode])
|
||||
}, [isEditMode, t])
|
||||
|
||||
const isQAModel = useMemo(() => {
|
||||
return docForm === ChunkingMode.qa
|
||||
@ -98,13 +97,11 @@ const SegmentDetail: FC<ISegmentDetailProps> = ({
|
||||
const total = formatNumber(isEditMode ? contentLength : segInfo!.word_count as number)
|
||||
const count = isEditMode ? contentLength : segInfo!.word_count as number
|
||||
return `${total} ${t('datasetDocuments.segment.characters', { count })}`
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isEditMode, question.length, answer.length, segInfo?.word_count, isQAModel])
|
||||
}, [isEditMode, question.length, answer.length, isQAModel, segInfo, t])
|
||||
|
||||
const labelPrefix = useMemo(() => {
|
||||
return isParentChildMode ? t('datasetDocuments.segment.parentChunk') : t('datasetDocuments.segment.chunk')
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isParentChildMode])
|
||||
}, [isParentChildMode, t])
|
||||
|
||||
return (
|
||||
<div className={'flex h-full flex-col'}>
|
||||
|
@ -42,7 +42,7 @@ const SegmentList = (
|
||||
embeddingAvailable,
|
||||
onClearFilter,
|
||||
}: ISegmentListProps & {
|
||||
ref: React.RefObject<unknown>;
|
||||
ref: React.LegacyRef<HTMLDivElement>
|
||||
},
|
||||
) => {
|
||||
const mode = useDocumentContext(s => s.mode)
|
||||
|
@ -202,7 +202,7 @@ export const OperationAction: FC<{
|
||||
const isListScene = scene === 'list'
|
||||
|
||||
const onOperate = async (operationName: OperationName) => {
|
||||
let opApi = deleteDocument
|
||||
let opApi
|
||||
switch (operationName) {
|
||||
case 'archive':
|
||||
opApi = archiveDocument
|
||||
@ -490,7 +490,7 @@ const DocumentList: FC<IDocumentListProps> = ({
|
||||
|
||||
const handleAction = (actionName: DocumentActionType) => {
|
||||
return async () => {
|
||||
let opApi = deleteDocument
|
||||
let opApi
|
||||
switch (actionName) {
|
||||
case DocumentActionType.archive:
|
||||
opApi = archiveDocument
|
||||
@ -527,7 +527,7 @@ const DocumentList: FC<IDocumentListProps> = ({
|
||||
<Checkbox
|
||||
className='mr-2 shrink-0'
|
||||
checked={isAllSelected}
|
||||
mixed={!isAllSelected && isSomeSelected}
|
||||
indeterminate={!isAllSelected && isSomeSelected}
|
||||
onCheck={onSelectedAll}
|
||||
/>
|
||||
)}
|
||||
|
Loading…
x
Reference in New Issue
Block a user