Ver Fonte

Merge pull request #8292 from weseek/feat/135930-incremental-search-results

feat: Incremental search results
Shun Miyazawa há 2 anos atrás
pai
commit
ad8a9db620

+ 4 - 2
apps/app/src/features/search/client/components/SearchModal.tsx

@@ -9,12 +9,13 @@ import { useSearchModal } from '../stores/search';
 import { SearchForm } from './SearchForm';
 import { SearchForm } from './SearchForm';
 import { SearchHelp } from './SearchHelp';
 import { SearchHelp } from './SearchHelp';
 import { SearchMethodMenuItem } from './SearchMethodMenuItem';
 import { SearchMethodMenuItem } from './SearchMethodMenuItem';
+import { SearchResultMenuItem } from './SearchResultMenuItem';
 
 
 const SearchModal = (): JSX.Element => {
 const SearchModal = (): JSX.Element => {
-  const { data: searchModalData, close: closeSearchModal } = useSearchModal();
-
   const [searchKeyword, setSearchKeyword] = useState('');
   const [searchKeyword, setSearchKeyword] = useState('');
 
 
+  const { data: searchModalData, close: closeSearchModal } = useSearchModal();
+
   const changeSearchTextHandler = useCallback((searchText: string) => {
   const changeSearchTextHandler = useCallback((searchText: string) => {
     setSearchKeyword(searchText);
     setSearchKeyword(searchText);
   }, []);
   }, []);
@@ -40,6 +41,7 @@ const SearchModal = (): JSX.Element => {
         <div className="border-top mt-3 mb-3" />
         <div className="border-top mt-3 mb-3" />
         <SearchMethodMenuItem searchKeyword={searchKeyword} />
         <SearchMethodMenuItem searchKeyword={searchKeyword} />
         <div className="border-top mt-2 mb-2" />
         <div className="border-top mt-2 mb-2" />
+        <SearchResultMenuItem searchKeyword={searchKeyword} />
         <SearchHelp />
         <SearchHelp />
       </ModalBody>
       </ModalBody>
     </Modal>
     </Modal>

+ 50 - 0
apps/app/src/features/search/client/components/SearchResultMenuItem.tsx

@@ -0,0 +1,50 @@
+import React from 'react';
+
+import { PagePathLabel, UserPicture } from '@growi/ui/dist/components';
+import { useDebounce } from 'usehooks-ts';
+
+import { useSWRxSearch } from '~/stores/search';
+
+
+type Props = {
+  searchKeyword: string,
+}
+export const SearchResultMenuItem = (props: Props): JSX.Element => {
+  const { searchKeyword } = props;
+
+  const debouncedKeyword = useDebounce(searchKeyword, 500);
+
+  const isEmptyKeyword = debouncedKeyword.trim() === '';
+
+  const { data: searchResult } = useSWRxSearch(isEmptyKeyword ? null : searchKeyword, null, { limit: 10 });
+
+  if (isEmptyKeyword || searchResult == null || searchResult.data.length === 0) {
+    return <></>;
+  }
+
+  return (
+    <>
+      <table>
+        <tbody>
+          {searchResult.data?.map(pageWithMeta => (
+            <tr key={pageWithMeta.data._id}>
+              <div className="ps-1 mb-2 d-flex">
+                <UserPicture user={pageWithMeta.data.creator} />
+
+                <span className="ms-3 text-break text-wrap">
+                  <PagePathLabel path={pageWithMeta.data.path} />
+                </span>
+
+                <span className="ms-2 text-muted d-flex justify-content-center align-items-center">
+                  <span className="material-symbols-outlined fs-5">footprint</span>
+                  <span>{pageWithMeta.data.seenUsers.length}</span>
+                </span>
+              </div>
+            </tr>
+          ))}
+        </tbody>
+      </table>
+      <div className="border-top mb-2" />
+    </>
+  );
+};