feature: search auto complete API
This commit is contained in:
parent
8e65767ac1
commit
04d72d3ca8
13
app/api/autocomplete/route.ts
Normal file
13
app/api/autocomplete/route.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { completeGoogle } from "search-engine-autocomplete";
|
||||||
|
import { NextRequest } from "next/server"
|
||||||
|
|
||||||
|
export async function GET(request: NextRequest) {
|
||||||
|
const query = request.nextUrl.searchParams.get('q')!;
|
||||||
|
let language = request.nextUrl.searchParams.get('l');
|
||||||
|
if (language === null) language = 'en-US';
|
||||||
|
const data = await completeGoogle(query, language);
|
||||||
|
const completeWord: string | undefined = data.suggestions.filter((s) => {
|
||||||
|
return s.relativeRelevance > 0.96
|
||||||
|
})[0]?.suggestion;
|
||||||
|
return new Response(completeWord ? completeWord : query);
|
||||||
|
}
|
9
app/api/suggestion/route.ts
Normal file
9
app/api/suggestion/route.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { completeGoogle } from "search-engine-autocomplete";
|
||||||
|
import { NextRequest } from "next/server"
|
||||||
|
|
||||||
|
export async function GET(request: NextRequest) {
|
||||||
|
const query = request.nextUrl.searchParams.get('q')!;
|
||||||
|
const language = "ja-JP";
|
||||||
|
const data = await completeGoogle(query, language);
|
||||||
|
return new Response(JSON.stringify(data));
|
||||||
|
}
|
@ -1,16 +1,24 @@
|
|||||||
import { useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import SuggestionBox from "./suggestionBox";
|
import SuggestionBox from "./suggestionBox";
|
||||||
import Suggestion from "./suggestion";
|
import Suggestion from "./suggestion";
|
||||||
|
import { useRecoilValue } from "recoil";
|
||||||
|
import { queryState } from "@/components/state/query";
|
||||||
|
|
||||||
export default function(){
|
export default function () {
|
||||||
const [suggestion, setSuggetsion] = useState([]);
|
const [suggestion, setSuggetsion] = useState([]);
|
||||||
|
const query = useRecoilValue(queryState);
|
||||||
|
useEffect(() => {
|
||||||
|
fetch(`/api/suggestion?q=${query}`)
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((data) => {
|
||||||
|
setSuggetsion(data);
|
||||||
|
});
|
||||||
|
}, [query]);
|
||||||
return (
|
return (
|
||||||
<SuggestionBox>
|
<SuggestionBox>
|
||||||
{
|
{suggestion.map((s: string) => {
|
||||||
suggestion.map((s: string) => {
|
return <Suggestion key={s}>{s}</Suggestion>;
|
||||||
return <Suggestion key={s}>{s}</Suggestion>
|
})}
|
||||||
})
|
|
||||||
}
|
|
||||||
</SuggestionBox>
|
</SuggestionBox>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useRecoilValue } from "recoil";
|
import { useRecoilState, useRecoilValue } from "recoil";
|
||||||
import { settingsState } from "../state/settings";
|
import { settingsState } from "../state/settings";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { normalizeURL } from "@/lib/normalizeURL";
|
import { normalizeURL } from "@/lib/normalizeURL";
|
||||||
import validLink from "@/lib/url/validLink";
|
import validLink from "@/lib/url/validLink";
|
||||||
|
import { queryState } from "../state/query";
|
||||||
|
|
||||||
export default function Search(props: { onFocus: () => void }) {
|
export default function Search(props: { onFocus: () => void }) {
|
||||||
const settings: settings = useRecoilValue(settingsState);
|
const settings: settings = useRecoilValue(settingsState);
|
||||||
const t = useTranslations("Search");
|
const t = useTranslations("Search");
|
||||||
const [query, setQuery] = useState("");
|
const [query, setQuery] = useRecoilState(queryState);
|
||||||
|
|
||||||
let style = "default";
|
let style = "default";
|
||||||
|
|
||||||
@ -41,7 +42,11 @@ export default function Search(props: { onFocus: () => void }) {
|
|||||||
placeholder={t("placeholder")}
|
placeholder={t("placeholder")}
|
||||||
onFocus={props.onFocus}
|
onFocus={props.onFocus}
|
||||||
onKeyDown={handleKeydown}
|
onKeyDown={handleKeydown}
|
||||||
onChange={(e) => setQuery(e.target.value)}
|
onChange={(e) =>
|
||||||
|
setQuery((_) => {
|
||||||
|
return e.target.value;
|
||||||
|
})
|
||||||
|
}
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
autoCorrect="off"
|
autoCorrect="off"
|
||||||
autoCapitalize="off"
|
autoCapitalize="off"
|
||||||
|
10
components/state/query.ts
Normal file
10
components/state/query.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { atom } from "recoil";
|
||||||
|
|
||||||
|
const queryState = atom({
|
||||||
|
key: "searchQuery",
|
||||||
|
default: ""
|
||||||
|
});
|
||||||
|
|
||||||
|
export {
|
||||||
|
queryState,
|
||||||
|
}
|
23
package.json
23
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "sparkhome",
|
"name": "sparkhome",
|
||||||
"version": "4.2.0",
|
"version": "4.10.0",
|
||||||
"private": false,
|
"private": false,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
@ -12,14 +12,15 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nextui-org/react": "^2.2.10",
|
"@nextui-org/react": "^2.2.10",
|
||||||
"clsx": "^2.1.0",
|
"clsx": "^2.1.0",
|
||||||
"framer-motion": "^11.0.24",
|
"framer-motion": "^11.0.25",
|
||||||
"next": "14.1.4",
|
"next": "14.1.4",
|
||||||
"next-intl": "^3.10.0",
|
"next-intl": "^3.11.1",
|
||||||
"next-themes": "^0.3.0",
|
"next-themes": "^0.3.0",
|
||||||
"punycode": "^2.3.1",
|
"punycode": "^2.3.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"recoil": "^0.7.7",
|
"recoil": "^0.7.7",
|
||||||
|
"search-engine-autocomplete": "^0.4.2",
|
||||||
"tailwind-merge": "^2.2.2",
|
"tailwind-merge": "^2.2.2",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"valid-url": "^1.0.9",
|
"valid-url": "^1.0.9",
|
||||||
@ -28,19 +29,19 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@jest/globals": "^29.7.0",
|
"@jest/globals": "^29.7.0",
|
||||||
"@testing-library/jest-dom": "^6.4.2",
|
"@testing-library/jest-dom": "^6.4.2",
|
||||||
"@testing-library/react": "^14.2.2",
|
"@testing-library/react": "^14.3.0",
|
||||||
"@types/jest": "^29.5.12",
|
"@types/jest": "^29.5.12",
|
||||||
"@types/node": "^20",
|
"@types/node": "^20.12.6",
|
||||||
"@types/punycode": "^2.1.4",
|
"@types/punycode": "^2.1.4",
|
||||||
"@types/react": "^18",
|
"@types/react": "^18.2.75",
|
||||||
"@types/react-dom": "^18",
|
"@types/react-dom": "^18.2.24",
|
||||||
"@types/valid-url": "^1.0.7",
|
"@types/valid-url": "^1.0.7",
|
||||||
"autoprefixer": "^10.0.1",
|
"autoprefixer": "^10.4.19",
|
||||||
"jest": "^29.7.0",
|
"jest": "^29.7.0",
|
||||||
"jest-environment-jsdom": "^29.7.0",
|
"jest-environment-jsdom": "^29.7.0",
|
||||||
"postcss": "^8",
|
"postcss": "^8.4.38",
|
||||||
"tailwindcss": "^3.3.0",
|
"tailwindcss": "^3.4.3",
|
||||||
"ts-jest": "^29.1.2",
|
"ts-jest": "^29.1.2",
|
||||||
"typescript": "^5"
|
"typescript": "^5.4.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
549
pnpm-lock.yaml
549
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user