SearchResultMenuItem.tsx 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. import { type JSX, useCallback } from 'react';
  2. import { PagePathLabel, UserPicture } from '@growi/ui/dist/components';
  3. import { useDebounce } from 'usehooks-ts';
  4. import { useSWRxSearch } from '~/stores/search';
  5. import type { GetItemProps } from '../interfaces/downshift';
  6. import { SearchMenuItem } from './SearchMenuItem';
  7. type Props = {
  8. activeIndex: number | null;
  9. searchKeyword: string;
  10. getItemProps: GetItemProps;
  11. };
  12. export const SearchResultMenuItem = (props: Props): JSX.Element => {
  13. const { activeIndex, searchKeyword, getItemProps } = props;
  14. const debouncedKeyword = useDebounce(searchKeyword, 500);
  15. const isEmptyKeyword = searchKeyword.trim().length === 0;
  16. const { data: searchResult, isLoading } = useSWRxSearch(
  17. isEmptyKeyword ? null : debouncedKeyword,
  18. null,
  19. { limit: 10 },
  20. );
  21. /**
  22. * SearchMenu is a combination of a list of SearchMethodMenuItem and SearchResultMenuItem (this component).
  23. * If no keywords are entered into SearchForm, SearchMethodMenuItem returns a single item. Conversely, when keywords are entered, three items are returned.
  24. * For these reasons, the starting index of SearchResultMemuItem changes depending on the presence or absence of the searchKeyword.
  25. */
  26. const getFiexdIndex = useCallback(
  27. (index: number) => {
  28. return (isEmptyKeyword ? 1 : 3) + index;
  29. },
  30. [isEmptyKeyword],
  31. );
  32. if (isLoading) {
  33. return (
  34. <>
  35. <div className="border-top mt-2 mb-2" />
  36. Searching...
  37. </>
  38. );
  39. }
  40. if (
  41. isEmptyKeyword ||
  42. searchResult == null ||
  43. searchResult.data.length === 0
  44. ) {
  45. return <></>;
  46. }
  47. return (
  48. <div>
  49. <div className="border-top mt-2 mb-2" />
  50. {searchResult?.data.map((item, index) => (
  51. <SearchMenuItem
  52. key={item.data._id}
  53. index={getFiexdIndex(index)}
  54. isActive={getFiexdIndex(index) === activeIndex}
  55. getItemProps={getItemProps}
  56. url={item.data._id}
  57. >
  58. <UserPicture user={item.data.creator} />
  59. <span className="ms-3 text-break text-wrap">
  60. <PagePathLabel path={item.data.path} />
  61. </span>
  62. <span className="text-body-tertiary ms-2 d-flex justify-content-center align-items-center">
  63. <span className="material-symbols-outlined fs-6 p-0">
  64. footprint
  65. </span>
  66. <span className="fs-6">{item.data.seenUsers.length}</span>
  67. </span>
  68. </SearchMenuItem>
  69. ))}
  70. </div>
  71. );
  72. };