### What problem does this PR solve? fix: Fetch chunk list by @tanstack/react-query #1306 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue)tags/v0.9.0
| import { useCallback } from 'react'; | |||||
| import { ResponseGetType } from '@/interfaces/database/base'; | |||||
| import { IChunk, IKnowledgeFile } from '@/interfaces/database/knowledge'; | |||||
| import kbService from '@/services/knowledge-service'; | |||||
| import { useQuery, useQueryClient } from '@tanstack/react-query'; | |||||
| import { useDebounce } from 'ahooks'; | |||||
| import { PaginationProps } from 'antd'; | |||||
| import { useCallback, useState } from 'react'; | |||||
| import { useDispatch } from 'umi'; | import { useDispatch } from 'umi'; | ||||
| import { | |||||
| useGetPaginationWithRouter, | |||||
| useHandleSearchChange, | |||||
| } from './logic-hooks'; | |||||
| import { useGetKnowledgeSearchParams } from './route-hook'; | import { useGetKnowledgeSearchParams } from './route-hook'; | ||||
| interface PayloadType { | |||||
| doc_id: string; | |||||
| keywords?: string; | |||||
| } | |||||
| export const useFetchChunkList = () => { | export const useFetchChunkList = () => { | ||||
| const dispatch = useDispatch(); | const dispatch = useDispatch(); | ||||
| const { documentId } = useGetKnowledgeSearchParams(); | const { documentId } = useGetKnowledgeSearchParams(); | ||||
| return fetchChunkList; | return fetchChunkList; | ||||
| }; | }; | ||||
| export interface IChunkListResult { | |||||
| searchString?: string; | |||||
| handleInputChange?: React.ChangeEventHandler<HTMLInputElement>; | |||||
| pagination: PaginationProps; | |||||
| setPagination?: (pagination: { page: number; pageSize: number }) => void; | |||||
| available: number | undefined; | |||||
| handleSetAvailable: (available: number | undefined) => void; | |||||
| } | |||||
| export const useFetchNextChunkList = (): ResponseGetType<{ | |||||
| data: IChunk[]; | |||||
| total: number; | |||||
| documentInfo: IKnowledgeFile; | |||||
| }> & | |||||
| IChunkListResult => { | |||||
| const { pagination, setPagination } = useGetPaginationWithRouter(); | |||||
| const { documentId } = useGetKnowledgeSearchParams(); | |||||
| const { searchString, handleInputChange } = useHandleSearchChange(); | |||||
| const [available, setAvailable] = useState<number | undefined>(); | |||||
| const debouncedSearchString = useDebounce(searchString, { wait: 500 }); | |||||
| const { data, isFetching: loading } = useQuery({ | |||||
| queryKey: [ | |||||
| 'fetchChunkList', | |||||
| documentId, | |||||
| pagination.current, | |||||
| pagination.pageSize, | |||||
| debouncedSearchString, | |||||
| available, | |||||
| ], | |||||
| initialData: { data: [], total: 0, documentInfo: {} }, | |||||
| // placeholderData: keepPreviousData, | |||||
| gcTime: 0, | |||||
| queryFn: async () => { | |||||
| const { data } = await kbService.chunk_list({ | |||||
| doc_id: documentId, | |||||
| page: pagination.current, | |||||
| size: pagination.pageSize, | |||||
| available_int: available, | |||||
| keywords: searchString, | |||||
| }); | |||||
| if (data.retcode === 0) { | |||||
| const res = data.data; | |||||
| return { | |||||
| data: res.chunks, | |||||
| total: res.total, | |||||
| documentInfo: res.doc, | |||||
| }; | |||||
| } | |||||
| return ( | |||||
| data?.data ?? { | |||||
| data: [], | |||||
| total: 0, | |||||
| documentInfo: {}, | |||||
| } | |||||
| ); | |||||
| }, | |||||
| }); | |||||
| const onInputChange: React.ChangeEventHandler<HTMLInputElement> = useCallback( | |||||
| (e) => { | |||||
| setPagination({ page: 1 }); | |||||
| handleInputChange(e); | |||||
| }, | |||||
| [handleInputChange, setPagination], | |||||
| ); | |||||
| const handleSetAvailable = useCallback( | |||||
| (a: number | undefined) => { | |||||
| setPagination({ page: 1 }); | |||||
| setAvailable(a); | |||||
| }, | |||||
| [setAvailable, setPagination], | |||||
| ); | |||||
| return { | |||||
| data, | |||||
| loading, | |||||
| pagination, | |||||
| setPagination, | |||||
| searchString, | |||||
| handleInputChange: onInputChange, | |||||
| available, | |||||
| handleSetAvailable, | |||||
| }; | |||||
| }; | |||||
| export const useSelectChunkList = () => { | |||||
| const queryClient = useQueryClient(); | |||||
| const data = queryClient.getQueriesData<{ | |||||
| data: IChunk[]; | |||||
| total: number; | |||||
| documentInfo: IKnowledgeFile; | |||||
| }>({ queryKey: ['fetchChunkList'] }); | |||||
| // console.log('🚀 ~ useSelectChunkList ~ data:', data); | |||||
| return data?.at(-1)?.[1]; | |||||
| }; |
| .contentEllipsis { | .contentEllipsis { | ||||
| .multipleLineEllipsis(3); | .multipleLineEllipsis(3); | ||||
| } | } | ||||
| .contentText { | .contentText { | ||||
| word-break: break-all; | |||||
| word-break: break-all !important; | |||||
| } | } | ||||
| .chunkCard { | .chunkCard { |
| import { ReactComponent as FilterIcon } from '@/assets/filter.svg'; | import { ReactComponent as FilterIcon } from '@/assets/filter.svg'; | ||||
| import { KnowledgeRouteKey } from '@/constants/knowledge'; | import { KnowledgeRouteKey } from '@/constants/knowledge'; | ||||
| import { IChunkListResult, useSelectChunkList } from '@/hooks/chunk-hooks'; | |||||
| import { useTranslate } from '@/hooks/common-hooks'; | import { useTranslate } from '@/hooks/common-hooks'; | ||||
| import { useKnowledgeBaseId } from '@/hooks/knowledge-hooks'; | import { useKnowledgeBaseId } from '@/hooks/knowledge-hooks'; | ||||
| import { | import { | ||||
| Space, | Space, | ||||
| Typography, | Typography, | ||||
| } from 'antd'; | } from 'antd'; | ||||
| import { ChangeEventHandler, useCallback, useMemo, useState } from 'react'; | |||||
| import { Link, useDispatch, useSelector } from 'umi'; | |||||
| import { useCallback, useMemo, useState } from 'react'; | |||||
| import { Link } from 'umi'; | |||||
| import { ChunkTextMode } from '../../constant'; | import { ChunkTextMode } from '../../constant'; | ||||
| import { ChunkModelState } from '../../model'; | |||||
| const { Text } = Typography; | const { Text } = Typography; | ||||
| interface IProps { | |||||
| interface IProps | |||||
| extends Pick< | |||||
| IChunkListResult, | |||||
| 'searchString' | 'handleInputChange' | 'available' | 'handleSetAvailable' | |||||
| > { | |||||
| checked: boolean; | checked: boolean; | ||||
| getChunkList: () => void; | |||||
| selectAllChunk: (checked: boolean) => void; | selectAllChunk: (checked: boolean) => void; | ||||
| createChunk: () => void; | createChunk: () => void; | ||||
| removeChunk: () => void; | removeChunk: () => void; | ||||
| } | } | ||||
| const ChunkToolBar = ({ | const ChunkToolBar = ({ | ||||
| getChunkList, | |||||
| selectAllChunk, | selectAllChunk, | ||||
| checked, | checked, | ||||
| createChunk, | createChunk, | ||||
| removeChunk, | removeChunk, | ||||
| switchChunk, | switchChunk, | ||||
| changeChunkTextMode, | changeChunkTextMode, | ||||
| available, | |||||
| handleSetAvailable, | |||||
| searchString, | |||||
| handleInputChange, | |||||
| }: IProps) => { | }: IProps) => { | ||||
| const { documentInfo, available, searchString }: ChunkModelState = | |||||
| useSelector((state: any) => state.chunkModel); | |||||
| const dispatch = useDispatch(); | |||||
| const data = useSelectChunkList(); | |||||
| const documentInfo = data?.documentInfo; | |||||
| const knowledgeBaseId = useKnowledgeBaseId(); | const knowledgeBaseId = useKnowledgeBaseId(); | ||||
| const [isShowSearchBox, setIsShowSearchBox] = useState(false); | const [isShowSearchBox, setIsShowSearchBox] = useState(false); | ||||
| const { t } = useTranslate('chunk'); | const { t } = useTranslate('chunk'); | ||||
| setIsShowSearchBox(true); | setIsShowSearchBox(true); | ||||
| }; | }; | ||||
| const handleSearchChange: ChangeEventHandler<HTMLInputElement> = (e) => { | |||||
| const val = e.target.value; | |||||
| dispatch({ type: 'chunkModel/setSearchString', payload: val }); | |||||
| dispatch({ | |||||
| type: 'chunkModel/throttledGetChunkList', | |||||
| payload: documentInfo.id, | |||||
| }); | |||||
| }; | |||||
| // const handleSearchChange: ChangeEventHandler<HTMLInputElement> = (e) => { | |||||
| // const val = e.target.value; | |||||
| // dispatch({ type: 'chunkModel/setSearchString', payload: val }); | |||||
| // dispatch({ | |||||
| // type: 'chunkModel/throttledGetChunkList', | |||||
| // payload: documentInfo.id, | |||||
| // }); | |||||
| // }; | |||||
| const handleSearchBlur = () => { | const handleSearchBlur = () => { | ||||
| if (!searchString.trim()) { | |||||
| if (!searchString?.trim()) { | |||||
| setIsShowSearchBox(false); | setIsShowSearchBox(false); | ||||
| } | } | ||||
| }; | }; | ||||
| const handleFilterChange = (e: RadioChangeEvent) => { | const handleFilterChange = (e: RadioChangeEvent) => { | ||||
| selectAllChunk(false); | selectAllChunk(false); | ||||
| dispatch({ type: 'chunkModel/setAvailable', payload: e.target.value }); | |||||
| getChunkList(); | |||||
| handleSetAvailable(e.target.value); | |||||
| }; | }; | ||||
| const filterContent = ( | const filterContent = ( | ||||
| <ArrowLeftOutlined /> | <ArrowLeftOutlined /> | ||||
| </Link> | </Link> | ||||
| <FilePdfOutlined /> | <FilePdfOutlined /> | ||||
| <Text ellipsis={{ tooltip: documentInfo.name }} style={{ width: 150 }}> | |||||
| {documentInfo.name} | |||||
| <Text ellipsis={{ tooltip: documentInfo?.name }} style={{ width: 150 }}> | |||||
| {documentInfo?.name} | |||||
| </Text> | </Text> | ||||
| </Space> | </Space> | ||||
| <Space> | <Space> | ||||
| placeholder={t('search')} | placeholder={t('search')} | ||||
| prefix={<SearchOutlined />} | prefix={<SearchOutlined />} | ||||
| allowClear | allowClear | ||||
| onChange={handleSearchChange} | |||||
| onChange={handleInputChange} | |||||
| onBlur={handleSearchBlur} | onBlur={handleSearchBlur} | ||||
| value={searchString} | value={searchString} | ||||
| /> | /> |
| import { Skeleton } from 'antd'; | import { Skeleton } from 'antd'; | ||||
| import { useEffect, useRef } from 'react'; | |||||
| import { memo, useEffect, useRef } from 'react'; | |||||
| import { | import { | ||||
| AreaHighlight, | AreaHighlight, | ||||
| Highlight, | Highlight, | ||||
| PdfLoader, | PdfLoader, | ||||
| Popup, | Popup, | ||||
| } from 'react-pdf-highlighter'; | } from 'react-pdf-highlighter'; | ||||
| import { useGetChunkHighlights } from '../../hooks'; | |||||
| import { useGetDocumentUrl } from './hooks'; | import { useGetDocumentUrl } from './hooks'; | ||||
| import { useCatchDocumentError } from '@/components/pdf-previewer/hooks'; | import { useCatchDocumentError } from '@/components/pdf-previewer/hooks'; | ||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| interface IProps { | interface IProps { | ||||
| selectedChunkId: string; | |||||
| highlights: IHighlight[]; | |||||
| setWidthAndHeight: (width: number, height: number) => void; | |||||
| } | } | ||||
| const HighlightPopup = ({ | const HighlightPopup = ({ | ||||
| comment, | comment, | ||||
| ) : null; | ) : null; | ||||
| // TODO: merge with DocumentPreviewer | // TODO: merge with DocumentPreviewer | ||||
| const Preview = ({ selectedChunkId }: IProps) => { | |||||
| const Preview = ({ highlights: state, setWidthAndHeight }: IProps) => { | |||||
| const url = useGetDocumentUrl(); | const url = useGetDocumentUrl(); | ||||
| useCatchDocumentError(url); | useCatchDocumentError(url); | ||||
| const { highlights: state, setWidthAndHeight } = | |||||
| useGetChunkHighlights(selectedChunkId); | |||||
| const ref = useRef<(highlight: IHighlight) => void>(() => {}); | const ref = useRef<(highlight: IHighlight) => void>(() => {}); | ||||
| const error = useCatchDocumentError(url); | const error = useCatchDocumentError(url); | ||||
| ); | ); | ||||
| }; | }; | ||||
| export default Preview; | |||||
| const compare = (oldProps: IProps, newProps: IProps) => { | |||||
| const arePropsEqual = | |||||
| oldProps.highlights === newProps.highlights || | |||||
| (oldProps.highlights.length === 0 && newProps.highlights.length === 0); | |||||
| return arePropsEqual; | |||||
| }; | |||||
| export default memo(Preview); |
| import { useSelectChunkList } from '@/hooks/chunk-hooks'; | |||||
| import { useOneNamespaceEffectsLoading } from '@/hooks/store-hooks'; | import { useOneNamespaceEffectsLoading } from '@/hooks/store-hooks'; | ||||
| import { IChunk, IKnowledgeFile } from '@/interfaces/database/knowledge'; | |||||
| import { IChunk } from '@/interfaces/database/knowledge'; | |||||
| import { buildChunkHighlights } from '@/utils/document-util'; | import { buildChunkHighlights } from '@/utils/document-util'; | ||||
| import { useCallback, useMemo, useState } from 'react'; | import { useCallback, useMemo, useState } from 'react'; | ||||
| import { IHighlight } from 'react-pdf-highlighter'; | import { IHighlight } from 'react-pdf-highlighter'; | ||||
| import { useSelector } from 'umi'; | |||||
| import { ChunkTextMode } from './constant'; | import { ChunkTextMode } from './constant'; | ||||
| export const useSelectDocumentInfo = () => { | |||||
| const documentInfo: IKnowledgeFile = useSelector( | |||||
| (state: any) => state.chunkModel.documentInfo, | |||||
| ); | |||||
| return documentInfo; | |||||
| }; | |||||
| export const useSelectChunkList = () => { | |||||
| const chunkList: IChunk[] = useSelector( | |||||
| (state: any) => state.chunkModel.data, | |||||
| ); | |||||
| return chunkList; | |||||
| }; | |||||
| // export const useSelectChunkList = () => { | |||||
| // const chunkList: IChunk[] = useSelector( | |||||
| // (state: any) => state.chunkModel.data, | |||||
| // ); | |||||
| // return chunkList; | |||||
| // }; | |||||
| export const useHandleChunkCardClick = () => { | export const useHandleChunkCardClick = () => { | ||||
| const [selectedChunkId, setSelectedChunkId] = useState<string>(''); | const [selectedChunkId, setSelectedChunkId] = useState<string>(''); | ||||
| }; | }; | ||||
| export const useGetSelectedChunk = (selectedChunkId: string) => { | export const useGetSelectedChunk = (selectedChunkId: string) => { | ||||
| const chunkList: IChunk[] = useSelectChunkList(); | |||||
| const data = useSelectChunkList(); | |||||
| return ( | return ( | ||||
| chunkList.find((x) => x.chunk_id === selectedChunkId) ?? ({} as IChunk) | |||||
| data?.data?.find((x) => x.chunk_id === selectedChunkId) ?? ({} as IChunk) | |||||
| ); | ); | ||||
| }; | }; | ||||
| return buildChunkHighlights(selectedChunk, size); | return buildChunkHighlights(selectedChunk, size); | ||||
| }, [selectedChunk, size]); | }, [selectedChunk, size]); | ||||
| const setWidthAndHeight = (width: number, height: number) => { | |||||
| const setWidthAndHeight = useCallback((width: number, height: number) => { | |||||
| setSize((pre) => { | setSize((pre) => { | ||||
| if (pre.height !== height || pre.width !== width) { | if (pre.height !== height || pre.width !== width) { | ||||
| return { height, width }; | return { height, width }; | ||||
| } | } | ||||
| return pre; | return pre; | ||||
| }); | }); | ||||
| }; | |||||
| }, []); | |||||
| return { highlights, setWidthAndHeight }; | return { highlights, setWidthAndHeight }; | ||||
| }; | }; |
| import { useFetchChunkList } from '@/hooks/chunk-hooks'; | |||||
| import { useFetchNextChunkList } from '@/hooks/chunk-hooks'; | |||||
| import { useDeleteChunkByIds } from '@/hooks/knowledge-hooks'; | import { useDeleteChunkByIds } from '@/hooks/knowledge-hooks'; | ||||
| import type { PaginationProps } from 'antd'; | import type { PaginationProps } from 'antd'; | ||||
| import { Divider, Flex, Pagination, Space, Spin, message } from 'antd'; | import { Divider, Flex, Pagination, Space, Spin, message } from 'antd'; | ||||
| import classNames from 'classnames'; | import classNames from 'classnames'; | ||||
| import { useCallback, useEffect, useState } from 'react'; | import { useCallback, useEffect, useState } from 'react'; | ||||
| import { useDispatch, useSearchParams, useSelector } from 'umi'; | |||||
| import { useDispatch, useSearchParams } from 'umi'; | |||||
| import ChunkCard from './components/chunk-card'; | import ChunkCard from './components/chunk-card'; | ||||
| import CreatingModal from './components/chunk-creating-modal'; | import CreatingModal from './components/chunk-creating-modal'; | ||||
| import ChunkToolBar from './components/chunk-toolbar'; | import ChunkToolBar from './components/chunk-toolbar'; | ||||
| import DocumentPreview from './components/document-preview/preview'; | import DocumentPreview from './components/document-preview/preview'; | ||||
| import { | import { | ||||
| useChangeChunkTextMode, | useChangeChunkTextMode, | ||||
| useGetChunkHighlights, | |||||
| useHandleChunkCardClick, | useHandleChunkCardClick, | ||||
| useSelectChunkListLoading, | |||||
| useSelectDocumentInfo, | |||||
| } from './hooks'; | } from './hooks'; | ||||
| import { ChunkModelState } from './model'; | |||||
| import { useTranslation } from 'react-i18next'; | import { useTranslation } from 'react-i18next'; | ||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| const Chunk = () => { | const Chunk = () => { | ||||
| const dispatch = useDispatch(); | const dispatch = useDispatch(); | ||||
| const chunkModel: ChunkModelState = useSelector( | |||||
| (state: any) => state.chunkModel, | |||||
| ); | |||||
| const [selectedChunkIds, setSelectedChunkIds] = useState<string[]>([]); | const [selectedChunkIds, setSelectedChunkIds] = useState<string[]>([]); | ||||
| const [searchParams] = useSearchParams(); | const [searchParams] = useSearchParams(); | ||||
| const { data = [], total, pagination } = chunkModel; | |||||
| const loading = useSelectChunkListLoading(); | |||||
| // const loading = useSelectChunkListLoading(); | |||||
| const documentId: string = searchParams.get('doc_id') || ''; | const documentId: string = searchParams.get('doc_id') || ''; | ||||
| const [chunkId, setChunkId] = useState<string | undefined>(); | const [chunkId, setChunkId] = useState<string | undefined>(); | ||||
| const { removeChunk } = useDeleteChunkByIds(); | const { removeChunk } = useDeleteChunkByIds(); | ||||
| const documentInfo = useSelectDocumentInfo(); | |||||
| const { | |||||
| data: { documentInfo, data = [], total }, | |||||
| pagination, | |||||
| loading, | |||||
| searchString, | |||||
| handleInputChange, | |||||
| available, | |||||
| handleSetAvailable, | |||||
| } = useFetchNextChunkList(); | |||||
| const { handleChunkCardClick, selectedChunkId } = useHandleChunkCardClick(); | const { handleChunkCardClick, selectedChunkId } = useHandleChunkCardClick(); | ||||
| const isPdf = documentInfo.type === 'pdf'; | |||||
| const isPdf = documentInfo?.type === 'pdf'; | |||||
| const { t } = useTranslation(); | const { t } = useTranslation(); | ||||
| const { changeChunkTextMode, textMode } = useChangeChunkTextMode(); | const { changeChunkTextMode, textMode } = useChangeChunkTextMode(); | ||||
| const getChunkList = useFetchChunkList(); | |||||
| const handleEditChunk = useCallback( | const handleEditChunk = useCallback( | ||||
| (chunk_id?: string) => { | (chunk_id?: string) => { | ||||
| setChunkId(chunk_id); | setChunkId(chunk_id); | ||||
| size, | size, | ||||
| ) => { | ) => { | ||||
| setSelectedChunkIds([]); | setSelectedChunkIds([]); | ||||
| dispatch({ | |||||
| type: 'chunkModel/setPagination', | |||||
| payload: { | |||||
| current: page, | |||||
| pageSize: size, | |||||
| }, | |||||
| }); | |||||
| getChunkList(); | |||||
| pagination.onChange?.(page, size); | |||||
| // getChunkList(); | |||||
| }; | }; | ||||
| const selectAllChunk = useCallback( | const selectAllChunk = useCallback( | ||||
| }, | }, | ||||
| }); | }); | ||||
| if (!chunkIds && resCode === 0) { | if (!chunkIds && resCode === 0) { | ||||
| getChunkList(); | |||||
| // getChunkList(); | |||||
| } | } | ||||
| }, | }, | ||||
| [ | [ | ||||
| dispatch, | dispatch, | ||||
| documentId, | documentId, | ||||
| getChunkList, | |||||
| // getChunkList, | |||||
| selectedChunkIds, | selectedChunkIds, | ||||
| showSelectedChunkWarning, | showSelectedChunkWarning, | ||||
| ], | ], | ||||
| ); | ); | ||||
| const { highlights, setWidthAndHeight } = | |||||
| useGetChunkHighlights(selectedChunkId); | |||||
| useEffect(() => { | useEffect(() => { | ||||
| getChunkList(); | |||||
| // getChunkList(); | |||||
| return () => { | return () => { | ||||
| dispatch({ | dispatch({ | ||||
| type: 'chunkModel/resetFilter', // TODO: need to reset state uniformly | type: 'chunkModel/resetFilter', // TODO: need to reset state uniformly | ||||
| }); | }); | ||||
| }; | }; | ||||
| }, [dispatch, getChunkList]); | |||||
| }, [dispatch]); | |||||
| return ( | return ( | ||||
| <> | <> | ||||
| <div className={styles.chunkPage}> | <div className={styles.chunkPage}> | ||||
| <ChunkToolBar | <ChunkToolBar | ||||
| getChunkList={getChunkList} | |||||
| selectAllChunk={selectAllChunk} | selectAllChunk={selectAllChunk} | ||||
| createChunk={handleEditChunk} | createChunk={handleEditChunk} | ||||
| removeChunk={handleRemoveChunk} | removeChunk={handleRemoveChunk} | ||||
| checked={selectedChunkIds.length === data.length} | checked={selectedChunkIds.length === data.length} | ||||
| switchChunk={switchChunk} | switchChunk={switchChunk} | ||||
| changeChunkTextMode={changeChunkTextMode} | changeChunkTextMode={changeChunkTextMode} | ||||
| searchString={searchString} | |||||
| handleInputChange={handleInputChange} | |||||
| available={available} | |||||
| handleSetAvailable={handleSetAvailable} | |||||
| ></ChunkToolBar> | ></ChunkToolBar> | ||||
| <Divider></Divider> | <Divider></Divider> | ||||
| <Flex flex={1} gap={'middle'}> | <Flex flex={1} gap={'middle'}> | ||||
| </Spin> | </Spin> | ||||
| <div className={styles.pageFooter}> | <div className={styles.pageFooter}> | ||||
| <Pagination | <Pagination | ||||
| responsive | |||||
| showLessItems | |||||
| showQuickJumper | |||||
| showSizeChanger | |||||
| onChange={onPaginationChange} | |||||
| pageSize={pagination.pageSize} | |||||
| pageSizeOptions={[10, 30, 60, 90]} | |||||
| current={pagination.current} | |||||
| size={'small'} | |||||
| {...pagination} | |||||
| total={total} | total={total} | ||||
| showTotal={(total) => ( | |||||
| <Space> | |||||
| {t('total', { keyPrefix: 'common' })} | |||||
| {total} | |||||
| </Space> | |||||
| )} | |||||
| size={'small'} | |||||
| onChange={onPaginationChange} | |||||
| /> | /> | ||||
| </div> | </div> | ||||
| </Flex> | </Flex> | ||||
| {isPdf && ( | |||||
| { | |||||
| <section className={styles.documentPreview}> | <section className={styles.documentPreview}> | ||||
| <DocumentPreview | <DocumentPreview | ||||
| selectedChunkId={selectedChunkId} | |||||
| highlights={highlights} | |||||
| setWidthAndHeight={setWidthAndHeight} | |||||
| ></DocumentPreview> | ></DocumentPreview> | ||||
| </section> | </section> | ||||
| )} | |||||
| } | |||||
| </Flex> | </Flex> | ||||
| </div> | </div> | ||||
| <CreatingModal doc_id={documentId} chunkId={chunkId} /> | <CreatingModal doc_id={documentId} chunkId={chunkId} /> |
| import { memo, useState } from 'react'; | |||||
| function compare(oldProps, newProps) { | |||||
| return true; | |||||
| } | |||||
| const Greeting = memo(function Greeting({ name }) { | |||||
| console.log('Greeting was rendered at', new Date().toLocaleTimeString()); | |||||
| return ( | |||||
| <h3> | |||||
| Hello{name && ', '} | |||||
| {name}! | |||||
| </h3> | |||||
| ); | |||||
| }, compare); | |||||
| export default function MyApp() { | |||||
| const [name, setName] = useState(''); | |||||
| const [address, setAddress] = useState(''); | |||||
| return ( | |||||
| <> | |||||
| <label> | |||||
| Name{': '} | |||||
| <input value={name} onChange={(e) => setName(e.target.value)} /> | |||||
| </label> | |||||
| <label> | |||||
| Address{': '} | |||||
| <input value={address} onChange={(e) => setAddress(e.target.value)} /> | |||||
| </label> | |||||
| <Greeting name={name} /> | |||||
| </> | |||||
| ); | |||||
| } |
| component: '@/pages/document-viewer', | component: '@/pages/document-viewer', | ||||
| layout: false, | layout: false, | ||||
| }, | }, | ||||
| { | |||||
| path: 'force', | |||||
| component: '@/pages/force-graph', | |||||
| layout: false, | |||||
| }, | |||||
| { | { | ||||
| path: '/*', | path: '/*', | ||||
| component: '@/pages/404', | component: '@/pages/404', |