1import {
2 AutocompleteState,
3 createAutocomplete,
4} from '@algolia/autocomplete-core';
5import { getAlgoliaResults } from '@algolia/autocomplete-js';
6import classNames from 'classnames';
7import { useRouter } from 'next/router';
8import { useMemo, useRef, useState } from 'react';
9import { searchClient } from '../algolia/client';
10import { Post } from '../types/post';
11
12const SearchAutocomplete = () => {
13 const router = useRouter();
14 const inputRef = useRef<HTMLInputElement>(null);
15 const [autocompleteState, setAutocompleteState] =
16 useState<AutocompleteState<Post>>();
17 const autocomplete = useMemo(
18 () =>
19 createAutocomplete<Post>({
20 id: 'autocomplete-search',
21 openOnFocus: true,
22 onStateChange({ state }) {
23 setAutocompleteState(state);
24 },
25 onSubmit(params) {
26 alert(`実際には「${params.state.query}」の検索結果画面に遷移します`);
27 },
28 getSources() {
29 return [
30 {
31 sourceId: 'posts',
32 getItemInputValue({ item }) {
33 return item.title;
34 },
35 getItems({ query }) {
36 return getAlgoliaResults({
37 searchClient,
38 queries: [
39 {
40 indexName: 'posts',
41 query,
42 params: {
43 hitsPerPage: 4,
44 },
45 },
46 ],
47 });
48 },
49 getItemUrl({ item }) {
50 return `/${router.query.id}?postId=${item.id}`;
51 },
52 onSelect(params) {
53 router.replace(params.itemUrl as string, undefined, {
54 shallow: true,
55 });
56 },
57 },
58 ];
59 },
60 navigator: {
61 navigate({ itemUrl }) {
62 router.push(itemUrl);
63 },
64 },
65 }),
66 []
67 );
68
69 return (
70 <div {...autocomplete.getRootProps({})}>
71 <form
72 {...(autocomplete.getFormProps({
73 inputElement: inputRef.current,
74 }) as any)}
75 >
76 <input
77 {...(autocomplete.getInputProps({
78 inputElement: inputRef.current,
79 }) as any)}
80 id="search-field"
81 className="block w-full bg-transparent rounded border mb-2"
82 placeholder="投稿を検索"
83 autoComplete="off"
84 ref={inputRef}
85 type="text"
86 />
87
88 <div {...(autocomplete.getPanelProps({}) as any)}>
89 {autocompleteState?.isOpen &&
90 autocompleteState?.collections.map((collection, index) => {
91 const { source, items } = collection;
92
93 return (
94 <div
95 className="rounded overflow-hidden dark:bg-slate-800 bg-white shadow w-full"
96 key={`source-${index}`}
97 >
98 {items.length > 0 && (
99 <ul {...autocomplete.getListProps()}>
100 {items.map((item, index) => (
101 <li key={item.title}>
102 <a
103 {...(autocomplete.getItemProps({
104 item,
105 source,
106 }) as any)}
107 className={classNames(
108 'block py-2 pl-3 text-sm',
109 autocompleteState?.activeItemId === index &&
110 'text-white bg-indigo-600'
111 )}
112 >
113 {item.title}
114 </a>
115 </li>
116 ))}
117 </ul>
118 )}
119 </div>
120 );
121 })}
122 </div>
123 </form>
124 </div>
125 );
126};
127
128export default SearchAutocomplete;