@@ -4,17 +4,105 @@ import { usePathname } from "next/navigation";
44import { pagesList } from "./pagesList" ;
55import { AccountMenu } from "./accountMenu" ;
66import { ThemeToggle } from "./[docs_id]/themeToggle" ;
7- import { useSidebarMdContext } from "./[docs_id]/dynamicMdContext" ;
7+ import {
8+ createContext ,
9+ ReactNode ,
10+ useCallback ,
11+ useContext ,
12+ useEffect ,
13+ useState ,
14+ } from "react" ;
15+ import { DynamicMarkdownSection } from "./[docs_id]/pageContent" ;
16+
17+ export interface ISidebarMdContext {
18+ loadedDocsId : string ;
19+ sidebarMdContent : DynamicMarkdownSection [ ] ;
20+ setSidebarMdContent : (
21+ docsId : string ,
22+ content :
23+ | DynamicMarkdownSection [ ]
24+ | ( ( prev : DynamicMarkdownSection [ ] ) => DynamicMarkdownSection [ ] )
25+ ) => void ;
26+ }
27+
28+ const SidebarMdContext = createContext < ISidebarMdContext | null > ( null ) ;
29+
30+ export function useSidebarMdContext ( ) {
31+ const context = useContext ( SidebarMdContext ) ;
32+ if ( ! context ) {
33+ throw new Error (
34+ "useSidebarMdContext must be used within a SidebarMdProvider"
35+ ) ;
36+ }
37+ return context ;
38+ }
39+
40+ export function useSidebarMdContextOptional ( ) {
41+ return useContext ( SidebarMdContext ) ;
42+ }
43+
44+ export function SidebarMdProvider ( { children } : { children : ReactNode } ) {
45+ const [ sidebarMdContent , setSidebarMdContent_ ] = useState <
46+ DynamicMarkdownSection [ ]
47+ > ( [ ] ) ;
48+ const [ loadedDocsId , setLoadedDocsId ] = useState < string > ( "" ) ;
49+ const setSidebarMdContent = useCallback (
50+ (
51+ docsId : string ,
52+ content :
53+ | DynamicMarkdownSection [ ]
54+ | ( ( prev : DynamicMarkdownSection [ ] ) => DynamicMarkdownSection [ ] )
55+ ) => {
56+ setLoadedDocsId ( docsId ) ;
57+ setSidebarMdContent_ ( content ) ;
58+ } ,
59+ [ ]
60+ ) ;
61+ return (
62+ < SidebarMdContext . Provider
63+ value = { {
64+ loadedDocsId,
65+ sidebarMdContent,
66+ setSidebarMdContent,
67+ } }
68+ >
69+ { children }
70+ </ SidebarMdContext . Provider >
71+ ) ;
72+ }
873
974export function Sidebar ( ) {
1075 const pathname = usePathname ( ) ;
11- const docs_id = pathname . replace ( / ^ \/ / , "" ) ;
12- const { sidebarMdContent } = useSidebarMdContext ( ) ;
13-
76+ const currentDocsId = pathname . replace ( / ^ \/ / , "" ) ; // ちょっと遅延がある
77+ const sidebarContext = useSidebarMdContext ( ) ;
78+ // sidebarMdContextの情報が古かったら使わない
79+ const sidebarMdContent =
80+ sidebarContext . loadedDocsId === currentDocsId
81+ ? sidebarContext . sidebarMdContent
82+ : [ ] ;
83+
1484 // 現在表示中のセクション(最初にinViewがtrueのもの)を見つける
1585 const currentSectionIndex = sidebarMdContent . findIndex (
1686 ( section ) => section . inView
1787 ) ;
88+
89+ // 目次の開閉状態
90+ const [ detailsOpen , setDetailsOpen ] = useState < boolean [ ] > ( [ ] ) ;
91+ const currentGroupIndex = pagesList . findIndex ( ( group ) =>
92+ currentDocsId . startsWith ( `${ group . id } -` )
93+ ) ;
94+ useEffect ( ( ) => {
95+ if ( currentGroupIndex !== - 1 && ! detailsOpen [ currentGroupIndex ] ) {
96+ const newDetailsOpen = [ ...detailsOpen ] ;
97+ // 現在のグループのdetailsは開いておく
98+ while ( newDetailsOpen . length <= currentGroupIndex ) {
99+ newDetailsOpen . push ( false ) ;
100+ }
101+ newDetailsOpen [ currentGroupIndex ] = true ;
102+ setDetailsOpen ( newDetailsOpen ) ;
103+ }
104+ } , [ currentGroupIndex , detailsOpen ] ) ;
105+
18106 return (
19107 < div className = "bg-base-200 h-full w-80 overflow-y-auto" >
20108 { /* todo: 背景色ほんとにこれでいい? */ }
@@ -51,9 +139,19 @@ export function Sidebar() {
51139 </ span >
52140
53141 < ul className = "menu w-full" >
54- { pagesList . map ( ( group ) => (
142+ { pagesList . map ( ( group , i ) => (
55143 < li key = { group . id } >
56- < details open = { docs_id . startsWith ( `${ group . id } -` ) } >
144+ < details
145+ open = { ! ! detailsOpen . at ( i ) }
146+ onToggle = { ( e ) => {
147+ const newDetailsOpen = [ ...detailsOpen ] ;
148+ while ( newDetailsOpen . length <= i ) {
149+ newDetailsOpen . push ( false ) ;
150+ }
151+ newDetailsOpen [ i ] = e . currentTarget . open ;
152+ setDetailsOpen ( newDetailsOpen ) ;
153+ } }
154+ >
57155 < summary > { group . lang } </ summary >
58156 < ul >
59157 { group . pages . map ( ( page ) => (
@@ -62,27 +160,31 @@ export function Sidebar() {
62160 < span className = "mr-0" > { page . id } .</ span >
63161 { page . title }
64162 </ Link >
65- { `${ group . id } -${ page . id } ` === docs_id && sidebarMdContent . length > 0 && (
66- < ul className = "ml-4 text-sm" >
67- { sidebarMdContent . slice ( 1 ) . map ( ( section , idx ) => {
68- // idx + 1 は実際のsectionIndexに対応(slice(1)で最初を除外しているため)
69- const isCurrentSection = idx + 1 === currentSectionIndex ;
70- return (
71- < li
72- key = { idx }
73- style = { { marginLeft : section . level - 2 + "em" } }
74- >
75- < Link
76- href = { `#${ idx + 1 } ` }
77- className = { isCurrentSection ? "font-bold" : "" }
163+ { `${ group . id } -${ page . id } ` === currentDocsId &&
164+ sidebarMdContent . length > 0 && (
165+ < ul className = "ml-4 text-sm" >
166+ { sidebarMdContent . slice ( 1 ) . map ( ( section , idx ) => {
167+ // idx + 1 は実際のsectionIndexに対応(slice(1)で最初を除外しているため)
168+ const isCurrentSection =
169+ idx + 1 === currentSectionIndex ;
170+ return (
171+ < li
172+ key = { idx }
173+ style = { { marginLeft : section . level - 2 + "em" } }
78174 >
79- { section . title }
80- </ Link >
81- </ li >
82- ) ;
83- } ) }
84- </ ul >
85- ) }
175+ < Link
176+ href = { `#${ idx + 1 } ` }
177+ className = {
178+ isCurrentSection ? "font-bold" : ""
179+ }
180+ >
181+ { section . title }
182+ </ Link >
183+ </ li >
184+ ) ;
185+ } ) }
186+ </ ul >
187+ ) }
86188 </ li >
87189 ) ) }
88190 </ ul >
0 commit comments