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
98 lines
3.9 KiB
TypeScript
98 lines
3.9 KiB
TypeScript
import { type FormEvent, useState } from 'react';
|
|
import { Link, useNavigate } from 'react-router';
|
|
import SelectLang from '../common/blocks/SelectLang';
|
|
import Config, { type MultiLang } from '../config';
|
|
import ItemUtil from '../database/lib/ItemUtil';
|
|
import Functions from '../functions';
|
|
|
|
interface Props {
|
|
lang: MultiLang;
|
|
}
|
|
|
|
const Header = (props: Props) => {
|
|
const { lang } = props;
|
|
const navigate = useNavigate();
|
|
const [keyword, setKeyword] = useState('');
|
|
const [showCreditsMenu, setShowCreditsMenu] = useState(false);
|
|
|
|
const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
|
|
event.preventDefault();
|
|
const trimmed = keyword.trim();
|
|
if (trimmed !== '') {
|
|
void navigate(ItemUtil.getSearchByKeywordUrl('all', trimmed));
|
|
}
|
|
};
|
|
|
|
return (
|
|
<header className="header">
|
|
<div className="clearfix">
|
|
<Link className="logo" to="/">
|
|
<span className="hidden">{Config.SITE_TITLE}</span>
|
|
</Link>
|
|
<div className="utility">
|
|
<SelectLang lang={lang} />
|
|
<div className="clearfix">
|
|
<a
|
|
className="incf-japan-node"
|
|
href="https://www.neuroinf.jp/"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
>
|
|
<span className="hidden">INCF Japan Node</span>
|
|
</a>
|
|
<div className="search-box">
|
|
<form onSubmit={handleSubmit}>
|
|
<input
|
|
type="text"
|
|
value={keyword}
|
|
onChange={(e) => setKeyword(e.target.value)}
|
|
placeholder="enter keywords here..."
|
|
/>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<ul className="mainmenu">
|
|
<li>
|
|
<Link to="/database/search/itemtype/binder">
|
|
{Functions.mlang('[en]Contents[/en][ja]コンテンツ[/ja]', lang)}
|
|
</Link>
|
|
</li>
|
|
<li>
|
|
<Link to="/categories">
|
|
{Functions.mlang('[en]Categories[/en][ja]カテゴリから検索[/ja]', lang)}
|
|
</Link>
|
|
</li>
|
|
<li>
|
|
<Link to="/overview">{Functions.mlang('[en]Overview[/en][ja]概要[/ja]', lang)}</Link>
|
|
</li>
|
|
<li>
|
|
<Link to="/news">{Functions.mlang('[en]News[/en][ja]ニュース[/ja]', lang)}</Link>
|
|
</li>
|
|
<li onMouseEnter={() => setShowCreditsMenu(true)} onMouseLeave={() => setShowCreditsMenu(false)}>
|
|
{Functions.mlang('[en]Credits[/en][ja]サイト情報[/ja]', lang)}
|
|
{showCreditsMenu && (
|
|
<div className="submenu">
|
|
<Link className="item" to="/credits/1">
|
|
Instruction for Use
|
|
</Link>
|
|
<Link className="item" to="/credits/2">
|
|
Copyrights
|
|
</Link>
|
|
<Link className="item" to="/credits/3">
|
|
Privacy Policy
|
|
</Link>
|
|
<Link className="item" to="/credits/4">
|
|
About Us
|
|
</Link>
|
|
</div>
|
|
)}
|
|
</li>
|
|
</ul>
|
|
</header>
|
|
);
|
|
};
|
|
|
|
export default Header;
|