22
33import { Article } from "../../../../article/article-client" ;
44import Image from "next/image" ;
5- import { useEffect , useRef } from "react" ;
5+ import { useEffect , useRef , useState } from "react" ; // useStateを追加
66import { generateArticleButton } from "../../../../article/article-client" ;
77import { useTranslations } from "next-intl" ;
88import { notFound } from "next/navigation" ;
@@ -11,8 +11,8 @@ import { AvailableLocales } from "@/i18n/request";
1111interface ClientComponentProps {
1212 articles : Article [ ] ;
1313 slug : string ;
14- tocs : { lang :AvailableLocales , toc :{ id : string ; text : string ; level : string } [ ] } [ ] ;
15- processedContents : { content :string , lang :AvailableLocales } [ ] ;
14+ tocs : { lang : AvailableLocales ; toc : { id : string ; text : string ; level : string } [ ] } [ ] ;
15+ processedContents : { content : string ; lang : AvailableLocales } [ ] ;
1616}
1717
1818export default function ClientComponent ( {
@@ -27,10 +27,54 @@ export default function ClientComponent({
2727 const processedContent = processedContents . find ( ( c ) => c . lang === locale ) ?. content ;
2828 const toc = tocs . find ( ( t ) => t . lang === locale ) ?. toc || [ ] ;
2929 const article = articles . find ( ( a ) => a . slug === slug && a . lang === locale ) ;
30- console . log ( article ) ;
31- const otherArticles = articles . filter (
32- ( a ) => a . slug !== slug && a . lang === locale
33- ) ;
30+ const otherArticles = articles . filter ( ( a ) => a . slug !== slug && a . lang === locale ) ;
31+
32+ // 検索機能用の状態
33+ const [ searchQuery , setSearchQuery ] = useState < string > ( "" ) ;
34+ const [ searchResults , setSearchResults ] = useState < string [ ] > ( [ ] ) ;
35+
36+ // 検索実行用の関数
37+ const handleSearch = ( query : string ) => {
38+ if ( ! query . trim ( ) ) {
39+ setSearchResults ( [ ] ) ;
40+ return ;
41+ }
42+
43+ try {
44+ // 正規表現を作成(エラーハンドリング付き)
45+ const regex = new RegExp ( query , "i" ) ; // 大文字小文字を無視
46+ const results : string [ ] = [ ] ;
47+
48+ // 記事のタイトルと内容を検索
49+ if ( article ?. title && regex . test ( article . title ) ) {
50+ results . push ( `Title: ${ article . title } ` ) ;
51+ }
52+
53+ if ( processedContent ) {
54+ // HTMLタグを除去して純粋なテキストを検索
55+ const div = document . createElement ( "div" ) ;
56+ div . innerHTML = processedContent ;
57+ const textContent = div . textContent || div . innerText || "" ;
58+ const lines = textContent . split ( "\n" ) . filter ( ( line ) => regex . test ( line ) ) ;
59+
60+ lines . forEach ( ( line ) => {
61+ if ( line . trim ( ) ) {
62+ results . push ( line . trim ( ) ) ;
63+ }
64+ } ) ;
65+ }
66+
67+ setSearchResults ( results ) ;
68+ } catch ( error ) {
69+ console . error ( "Invalid regular expression:" , error ) ;
70+ setSearchResults ( [ "Invalid regular expression" ] ) ;
71+ }
72+ } ;
73+
74+ // 検索クエリが変更されたときに検索を実行
75+ useEffect ( ( ) => {
76+ handleSearch ( searchQuery ) ;
77+ } , [ searchQuery ] ) ;
3478
3579 useEffect ( ( ) => {
3680 const handleScroll = ( ) => {
@@ -69,6 +113,27 @@ export default function ClientComponent({
69113
70114 return (
71115 < div className = "article-container" >
116+ { /* 検索バーを追加 */ }
117+ < div className = "search-bar" >
118+ < input
119+ type = "text"
120+ placeholder = { t ( "pages.article.content.words.searchPlaceholder" ) || "Search article..." }
121+ value = { searchQuery }
122+ onChange = { ( e ) => setSearchQuery ( e . target . value ) }
123+ className = "search-input"
124+ />
125+ { searchResults . length > 0 && (
126+ < div className = "search-results" >
127+ < h3 > { t ( "pages.article.content.words.searchResults" ) || "Search Results" } </ h3 >
128+ < ul >
129+ { searchResults . map ( ( result , index ) => (
130+ < li key = { index } > { result } </ li >
131+ ) ) }
132+ </ ul >
133+ </ div >
134+ ) }
135+ </ div >
136+
72137 < aside className = "toc relative md:sticky" ref = { tocRef } >
73138 < h2 > { t ( "pages.article.content.words.index" ) } </ h2 >
74139 < ul >
@@ -105,4 +170,4 @@ export default function ClientComponent({
105170 </ section >
106171 </ div >
107172 ) ;
108- }
173+ }
0 commit comments