@@ -25,6 +25,26 @@ export function ToggleableLinkItem(
2525
2626 const currentPagePath = useCurrentPagePath ( ) ;
2727 const isActive = pathnames . some ( ( pathname ) => pathname === currentPagePath ) ;
28+ const defaultIsOpen =
29+ isActive || pathnames . some ( ( pathname ) => currentPagePath . startsWith ( `${ pathname } /` ) ) ;
30+ const [ isOpen , setIsOpen ] = React . useState ( defaultIsOpen ) ;
31+ const hasBeenToggled = useRef ( false ) ;
32+
33+ // Update the visibility of the children if one of the descendants becomes active.
34+ React . useEffect ( ( ) => {
35+ if ( defaultIsOpen && ! hasBeenToggled . current ) {
36+ setIsOpen ( defaultIsOpen ) ;
37+ }
38+ } , [ defaultIsOpen ] ) ;
39+
40+ const handleToggle = ( newState : boolean | ( ( prev : boolean ) => boolean ) ) => {
41+ hasBeenToggled . current = true ;
42+ if ( typeof newState === 'function' ) {
43+ setIsOpen ( newState ) ;
44+ } else {
45+ setIsOpen ( newState ) ;
46+ }
47+ } ;
2848
2949 if ( ! descendants ) {
3050 return (
@@ -35,15 +55,15 @@ export function ToggleableLinkItem(
3555 }
3656
3757 return (
38- < DescendantsRenderer
39- descendants = { descendants }
40- defaultIsOpen = {
41- isActive || pathnames . some ( ( pathname ) => currentPagePath . startsWith ( `${ pathname } /` ) )
42- }
43- >
58+ < DescendantsRenderer descendants = { descendants } isOpen = { isOpen } setIsOpen = { handleToggle } >
4459 { ( { descendants, toggler } ) => (
4560 < >
46- < LinkItem href = { href } insights = { insights } isActive = { isActive } >
61+ < LinkItem
62+ href = { href }
63+ insights = { insights }
64+ isActive = { isActive }
65+ onActiveClick = { ( ) => handleToggle ( ! isOpen ) }
66+ >
4767 { children }
4868 { toggler }
4969 </ LinkItem >
@@ -57,11 +77,20 @@ export function ToggleableLinkItem(
5777function LinkItem (
5878 props : Pick < LinkProps , 'href' | 'insights' | 'children' > & {
5979 isActive : boolean ;
80+ onActiveClick ?: ( ) => void ;
6081 }
6182) {
62- const { isActive, href, insights, children } = props ;
83+ const { isActive, href, insights, children, onActiveClick } = props ;
6384 const anchorRef = useRef < HTMLAnchorElement > ( null ) ;
6485 useScrollToActiveTOCItem ( { anchorRef, isActive } ) ;
86+
87+ const handleClick = ( event : React . MouseEvent < HTMLAnchorElement > ) => {
88+ if ( isActive && onActiveClick ) {
89+ event . preventDefault ( ) ;
90+ onActiveClick ( ) ;
91+ }
92+ } ;
93+
6594 return (
6695 < Link
6796 ref = { anchorRef }
@@ -72,31 +101,25 @@ function LinkItem(
72101 'ToggleableLinkItemStyles' ,
73102 ...( isActive ? [ 'ToggleableLinkItemActiveStyles' as const ] : [ ] ) ,
74103 ] }
104+ onClick = { handleClick }
75105 >
76106 { children }
77107 </ Link >
78108 ) ;
79109}
80110
81111function DescendantsRenderer ( props : {
82- defaultIsOpen : boolean ;
83112 descendants : React . ReactNode ;
113+ isOpen : boolean ;
114+ setIsOpen : React . Dispatch < React . SetStateAction < boolean > > ;
84115 children : ( renderProps : {
85116 descendants : React . ReactNode ;
86117 toggler : React . ReactNode ;
87118 } ) => React . ReactNode ;
88119} ) {
89- const { defaultIsOpen, children, descendants } = props ;
90- const [ isOpen , setIsOpen ] = React . useState ( defaultIsOpen ) ;
91-
92- // Update the visibility of the children if one of the descendants becomes active.
93- React . useEffect ( ( ) => {
94- if ( defaultIsOpen ) {
95- setIsOpen ( defaultIsOpen ) ;
96- }
97- } , [ defaultIsOpen ] ) ;
120+ const { descendants, isOpen, setIsOpen } = props ;
98121
99- return children ( {
122+ return props . children ( {
100123 toggler : (
101124 < Toggler
102125 isLinkActive = { isOpen }
0 commit comments