import React, { useCallback, useMemo, useRef, useState, } from 'react'; import { useTranslation } from 'next-i18next'; import type { ISelectableAll, ISelectableAndIndeterminatable } from '~/client/interfaces/selectable-all'; import { useKeywordManager } from '~/client/services/search-operation'; import type { IFormattedSearchResult } from '~/interfaces/search'; import { useShowPageLimitationL } from '~/stores/context'; import { type ISearchConditions, type ISearchConfigurations, useSWRxSearch } from '~/stores/search'; import { NotAvailableForGuest } from './NotAvailableForGuest'; import { NotAvailableForReadOnlyUser } from './NotAvailableForReadOnlyUser'; import PaginationWrapper from './PaginationWrapper'; import { OperateAllControl } from './SearchPage/OperateAllControl'; import SearchControl from './SearchPage/SearchControl'; import { type IReturnSelectedPageIds, SearchPageBase, usePageDeleteModalForBulkDeletion } from './SearchPage/SearchPageBase'; import styles from './SearchPage.module.scss'; // TODO: replace with "customize:showPageLimitationS" const INITIAL_PAGIONG_SIZE = 20; /** * SearchResultListHead */ type SearchResultListHeadProps = { searchResult: IFormattedSearchResult, pagingSize: number, onPagingSizeChanged: (size: number) => void, } const SearchResultListHead = React.memo((props: SearchResultListHeadProps): JSX.Element => { const { t } = useTranslation(); const { searchResult, // pagingSize, onPagingSizeChanged, } = props; const { took, total } = searchResult.meta; if (total === 0) { return (
0 {t('search_result.page_number_unit')}
); } return (
{total} {t('search_result.hit_number_unit', 'hit')} { took != null && ( // blackout 70px rectangle in VRT ({took}ms) ) }
{/* TODO: infinite scroll for search result */} {/*
*/}
); }); SearchResultListHead.displayName = 'SearchResultListHead'; export const SearchPage = (): JSX.Element => { const { t } = useTranslation(); const { data: showPageLimitationL } = useShowPageLimitationL(); const { data: keyword, pushState } = useKeywordManager(); const [offset, setOffset] = useState(0); const [limit, setLimit] = useState(showPageLimitationL ?? INITIAL_PAGIONG_SIZE); const [configurationsByControl, setConfigurationsByControl] = useState>({}); const [isCollapsed, setIsCollapsed] = useState(false); const [selectedCount, setSelectedCount] = useState(0); const selectAllControlRef = useRef(null); const searchPageBaseRef = useRef(null); const { data, conditions, mutate } = useSWRxSearch(keyword ?? '', null, { ...configurationsByControl, offset, limit, }); const searchInvokedHandler = useCallback((newKeyword: string, newConfigurations: Partial) => { setOffset(0); setConfigurationsByControl(newConfigurations); pushState(newKeyword); mutate(); }, [mutate, pushState]); const selectAllCheckboxChangedHandler = useCallback((isChecked: boolean) => { const instance = searchPageBaseRef.current; if (instance == null) { return; } if (isChecked) { instance.selectAll(); } else { instance.deselectAll(); } // update selected count setSelectedCount(instance.getSelectedPageIds?.().size ?? 0); }, []); const selectedPagesByCheckboxesChangedHandler = useCallback((selectedCount: number, totalCount: number) => { const instance = selectAllControlRef.current; if (instance == null) { return; } if (selectedCount === 0) { instance.deselect(); } else if (selectedCount === totalCount) { instance.select(); } else { setIsCollapsed(true); instance.setIndeterminate(); } // update selected count setSelectedCount(selectedCount); }, []); const pagingSizeChangedHandler = useCallback((pagingSize: number) => { setOffset(0); setLimit(pagingSize); mutate(); }, [mutate]); const pagingNumberChangedHandler = useCallback((activePage: number) => { setOffset((activePage - 1) * limit); mutate(); }, [limit, mutate]); const initialSearchConditions: Partial = useMemo(() => { return { keyword, limit: INITIAL_PAGIONG_SIZE, }; }, [keyword]); // for bulk deletion const deleteAllButtonClickedHandler = usePageDeleteModalForBulkDeletion(data, searchPageBaseRef, () => mutate()); const hitsCount = data?.meta.hitsCount; const extraControls = useMemo(() => { return ( ); }, [isCollapsed]); const collapseContents = useMemo(() => { return (
); }, [deleteAllButtonClickedHandler, hitsCount, selectAllCheckboxChangedHandler, selectedCount, t]); const searchControl = useMemo(() => { return ( ); }, [extraControls, collapseContents, initialSearchConditions, isCollapsed, searchInvokedHandler]); const searchResultListHead = useMemo(() => { if (data == null) { return <>; } return ( ); }, [data, limit, pagingSizeChangedHandler]); const searchPager = useMemo(() => { // when pager is not needed if (data == null || data.meta.hitsCount === data.meta.total) { return <>; } const { total } = data.meta; const { offset, limit } = conditions; return ( ); }, [conditions, data, pagingNumberChangedHandler]); return ( ); };