Files
open.neuroinf.jp/src/database/blocks/Search.tsx
T
orrisroot e93014bcea feat: migrate from Create React App to Vite
Rebuild the site on the stack the other converted sites share: Vite 8,
React 19, TypeScript 7, react-router 8, Biome and sanitize.css, with the
site data moved out of the bundle into static-contents/.

- base the database and pico code on the other converted sites,
  keeping this site's layout, pages and item fields
- read file timestamps as Unix seconds in the file and license views
- serve all module data from modules/, redirecting the old download and
  /database/file URLs
- keep form controls on white
- track page views with GA4
2026-09-14 17:38:48 +09:00

70 lines
2.6 KiB
TypeScript

import { type ChangeEvent, type FormEvent, useEffect, useState } from 'react';
import { Link, useLocation, useNavigate } from 'react-router';
import Config, { type MultiLang } from '../../config';
import Functions from '../../functions';
import ItemUtil, { type KeywordSearchType } from '../lib/ItemUtil';
interface Props {
lang: MultiLang;
}
const SEARCH_PATH = '/database/search';
const Search = (props: Props) => {
const { lang } = props;
const location = useLocation();
const navigate = useNavigate();
const [type, setType] = useState<KeywordSearchType>('all');
const [keyword, setKeyword] = useState('');
// Landing on the results page adopts the query it was called with; moving
// anywhere else leaves whatever the visitor has typed alone.
useEffect(() => {
if (location.pathname === SEARCH_PATH) {
const parsed = ItemUtil.getSearchKeywordByQuery(location.search);
setType(parsed.type);
setKeyword(parsed.keyword);
}
}, [location.pathname, location.search]);
const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
void navigate(ItemUtil.getSearchByKeywordUrl(type, keyword));
};
const options = [
{ value: 'all', label: '[en]All[/en][ja]全て[/ja]' },
{ value: 'basic', label: '[en]Title & Keyword[/en][ja]タイトル&キーワード[/ja]' },
];
Config.XOONIPS_ITEMTYPES.forEach((itemType) => {
options.push({ value: itemType, label: Functions.pascalCase(itemType) });
});
return (
<form onSubmit={handleSubmit}>
<input
style={{ width: '170px' }}
type="text"
value={keyword}
onChange={(event: ChangeEvent<HTMLInputElement>) => setKeyword(event.target.value.trim())}
/>
&nbsp;&nbsp;
<select
value={type}
onChange={(event: ChangeEvent<HTMLSelectElement>) => setType(event.target.value as KeywordSearchType)}
>
{options.map((option) => (
<option key={option.value} value={option.value}>
{Functions.mlang(option.label, lang)}
</option>
))}
</select>
<br />
<input className="formButton" type="submit" value="Search" />
&nbsp;&nbsp;&nbsp;&nbsp;
<Link to="/database/advanced">{Functions.mlang('[en]Advanced[/en][ja]詳細検索[/ja]', lang)}</Link>
</form>
);
};
export default Search;