@@ -10,15 +10,112 @@ import { MAIN_LAYOUT_ID } from '@/layouts/conf';
1010import { Heading } from './Heading' ;
1111
1212export const TableContent = ( ) => {
13+ const { spacingsTokens, colorsTokens } = useCunninghamTheme ( ) ;
14+ const [ containerHeight , setContainerHeight ] = useState ( '100vh' ) ;
15+
16+ const { t } = useTranslation ( ) ;
17+ const [ isOpen , setIsOpen ] = useState ( false ) ;
18+
19+ /**
20+ * Calculate container height based on the scrollable content
21+ */
22+ useEffect ( ( ) => {
23+ const mainLayout = document . getElementById ( MAIN_LAYOUT_ID ) ;
24+ if ( mainLayout ) {
25+ setContainerHeight ( `${ mainLayout . scrollHeight } px` ) ;
26+ }
27+ } , [ ] ) ;
28+
29+ const onOpen = ( ) => {
30+ setIsOpen ( true ) ;
31+ } ;
32+
33+ return (
34+ < Box
35+ $height = { containerHeight }
36+ $position = "absolute"
37+ $css = { css `
38+ top : 72px ;
39+ right : 20px ;
40+ ` }
41+ >
42+ < Box
43+ as = "nav"
44+ id = "summaryContainer"
45+ $width = { ! isOpen ? '40px' : '200px' }
46+ $height = { ! isOpen ? '40px' : 'auto' }
47+ $maxHeight = "calc(50vh - 60px)"
48+ $zIndex = { 1000 }
49+ $align = "center"
50+ $padding = { isOpen ? 'xs' : '0' }
51+ $justify = "center"
52+ $position = "sticky"
53+ aria-label = { t ( 'Summary' ) }
54+ $css = { css `
55+ top : var (--c--globals--spacings--0 );
56+ border : 1px solid ${ colorsTokens [ 'brand-100' ] } ;
57+ overflow : hidden;
58+ border-radius : ${ spacingsTokens [ '3xs' ] } ;
59+ background : ${ colorsTokens [ 'gray-000' ] } ;
60+ ${ isOpen &&
61+ css `
62+ display : flex;
63+ flex-direction : column;
64+ justify-content : flex-start;
65+ align-items : flex-start;
66+ gap : ${ spacingsTokens [ '2xs' ] } ;
67+ ` }
68+ ` }
69+ className = "--docs--table-content"
70+ >
71+ { ! isOpen && (
72+ < BoxButton
73+ onClick = { onOpen }
74+ $width = "100%"
75+ $height = "100%"
76+ $justify = "center"
77+ $align = "center"
78+ aria-label = { t ( 'Summary' ) }
79+ aria-expanded = { isOpen }
80+ aria-controls = "toc-list"
81+ $css = { css `
82+ & : focus-visible {
83+ outline : none;
84+ box-shadow : 0 0 0 4px ${ colorsTokens [ 'brand-400' ] } ;
85+ background : ${ colorsTokens [ 'brand-100' ] } ;
86+ width : 90% ;
87+ height : 90% ;
88+ }
89+ ` }
90+ >
91+ < Icon
92+ $theme = "brand"
93+ $variation = "tertiary"
94+ iconName = "list"
95+ variant = "symbols-outlined"
96+ />
97+ </ BoxButton >
98+ ) }
99+ { isOpen && < TableContentOpened setIsOpen = { setIsOpen } /> }
100+ </ Box >
101+ </ Box >
102+ ) ;
103+ } ;
104+
105+ const TableContentOpened = ( {
106+ setIsOpen,
107+ } : {
108+ setIsOpen : ( isOpen : boolean ) => void ;
109+ } ) => {
13110 const { headings } = useHeadingStore ( ) ;
14111 const { editor } = useEditorStore ( ) ;
15112 const { spacingsTokens, colorsTokens } = useCunninghamTheme ( ) ;
16-
17113 const [ headingIdHighlight , setHeadingIdHighlight ] = useState < string > ( ) ;
18-
19114 const { t } = useTranslation ( ) ;
20- const [ isHover , setIsHover ] = useState ( false ) ;
21115
116+ /**
117+ * Handle scroll to highlight the current heading in the table of content
118+ */
22119 useEffect ( ( ) => {
23120 const handleScroll = ( ) => {
24121 if ( ! headings ) {
@@ -69,23 +166,10 @@ export const TableContent = () => {
69166 . getElementById ( MAIN_LAYOUT_ID )
70167 ?. removeEventListener ( 'scroll' , scrollFn ) ;
71168 } ;
72- } , [ headings , setHeadingIdHighlight ] ) ;
73-
74- const onOpen = ( ) => {
75- setIsHover ( true ) ;
76- setTimeout ( ( ) => {
77- const element = document . getElementById ( `heading-${ headingIdHighlight } ` ) ;
78-
79- element ?. scrollIntoView ( {
80- behavior : 'instant' ,
81- inline : 'center' ,
82- block : 'center' ,
83- } ) ;
84- } , 0 ) ; // 300ms is the transition time of the box
85- } ;
169+ } , [ headings ] ) ;
86170
87171 const onClose = ( ) => {
88- setIsHover ( false ) ;
172+ setIsOpen ( false ) ;
89173 } ;
90174
91175 if (
@@ -99,129 +183,69 @@ export const TableContent = () => {
99183
100184 return (
101185 < Box
102- as = "nav"
103- id = "summaryContainer"
104- $width = { ! isHover ? '40px' : '200px' }
105- $height = { ! isHover ? '40px' : 'auto' }
106- $maxHeight = "calc(50vh - 60px)"
107- $zIndex = { 1000 }
108- $align = "center"
109- $padding = { isHover ? 'xs' : '0' }
110- $justify = "center"
111- $position = "sticky"
112- aria-label = { t ( 'Summary' ) }
186+ $width = "100%"
187+ $overflow = "hidden"
113188 $css = { css `
114- top : var (--c--globals--spacings--0 );
115- border : 1px solid ${ colorsTokens [ 'brand-100' ] } ;
116- overflow : hidden;
117- border-radius : ${ spacingsTokens [ '3xs' ] } ;
118- background : ${ colorsTokens [ 'gray-000' ] } ;
119- ${ isHover &&
120- css `
121- display : flex;
122- flex-direction : column;
123- justify-content : flex-start;
124- align-items : flex-start;
125- gap : ${ spacingsTokens [ '2xs' ] } ;
126- ` }
189+ user-select : none;
190+ padding : ${ spacingsTokens [ '4xs' ] } ;
127191 ` }
128- className = "--docs--table-content"
129192 >
130- { ! isHover && (
193+ < Box
194+ $margin = { { bottom : spacingsTokens . xs } }
195+ $direction = "row"
196+ $justify = "space-between"
197+ $align = "center"
198+ >
199+ < Text $weight = "500" $size = "sm" >
200+ { t ( 'Summary' ) }
201+ </ Text >
131202 < BoxButton
132- onClick = { onOpen }
133- $width = "100%"
134- $height = "100%"
203+ onClick = { onClose }
135204 $justify = "center"
136205 $align = "center"
137206 aria-label = { t ( 'Summary' ) }
138- aria-expanded = { isHover }
207+ aria-expanded = "true"
139208 aria-controls = "toc-list"
140209 $css = { css `
210+ transition : none !important ;
211+ transform : rotate (180deg );
141212 & : focus-visible {
142213 outline : none;
143- box-shadow : 0 0 0 4px ${ colorsTokens [ 'brand-400' ] } ;
144- background : ${ colorsTokens [ 'brand-100' ] } ;
145- width : 90% ;
146- height : 90% ;
214+ box-shadow : 0 0 0 2px ${ colorsTokens [ 'brand-400' ] } ;
215+ border-radius : var (--c--globals--spacings--st );
147216 }
148217 ` }
149218 >
150- < Icon
151- $theme = "brand"
152- $variation = "tertiary"
153- iconName = "list"
154- variant = "symbols-outlined"
155- />
219+ < Icon iconName = "menu_open" $theme = "brand" $variation = "tertiary" />
156220 </ BoxButton >
157- ) }
158- { isHover && (
159- < Box
160- $width = "100%"
161- $overflow = "hidden"
162- $css = { css `
163- user-select : none;
164- padding : ${ spacingsTokens [ '4xs' ] } ;
165- ` }
166- >
167- < Box
168- $margin = { { bottom : spacingsTokens . xs } }
169- $direction = "row"
170- $justify = "space-between"
171- $align = "center"
172- >
173- < Text $weight = "500" $size = "sm" >
174- { t ( 'Summary' ) }
175- </ Text >
176- < BoxButton
177- onClick = { onClose }
178- $justify = "center"
179- $align = "center"
180- aria-label = { t ( 'Summary' ) }
181- aria-expanded = { isHover }
182- aria-controls = "toc-list"
183- $css = { css `
184- transition : none !important ;
185- transform : rotate (180deg );
186- & : focus-visible {
187- outline : none;
188- box-shadow : 0 0 0 2px ${ colorsTokens [ 'brand-400' ] } ;
189- border-radius : var (--c--globals--spacings--st );
190- }
191- ` }
192- >
193- < Icon iconName = "menu_open" $theme = "brand" $variation = "tertiary" />
194- </ BoxButton >
195- </ Box >
196- < Box
197- as = "ul"
198- id = "toc-list"
199- role = "list"
200- $gap = { spacingsTokens [ '3xs' ] }
201- $css = { css `
202- overflow-y : auto;
203- list-style : none;
204- padding : ${ spacingsTokens [ '3xs' ] } ;
205- margin : 0 ;
206- ` }
207- >
208- { headings ?. map (
209- ( heading ) =>
210- heading . contentText && (
211- < Box as = "li" role = "listitem" key = { heading . id } >
212- < Heading
213- editor = { editor }
214- headingId = { heading . id }
215- level = { heading . props . level }
216- text = { heading . contentText }
217- isHighlight = { headingIdHighlight === heading . id }
218- />
219- </ Box >
220- ) ,
221- ) }
222- </ Box >
223- </ Box >
224- ) }
221+ </ Box >
222+ < Box
223+ as = "ul"
224+ id = "toc-list"
225+ role = "list"
226+ $gap = { spacingsTokens [ '3xs' ] }
227+ $css = { css `
228+ overflow-y : auto;
229+ list-style : none;
230+ padding : ${ spacingsTokens [ '3xs' ] } ;
231+ margin : 0 ;
232+ ` }
233+ >
234+ { headings ?. map (
235+ ( heading ) =>
236+ heading . contentText && (
237+ < Box as = "li" role = "listitem" key = { heading . id } >
238+ < Heading
239+ editor = { editor }
240+ headingId = { heading . id }
241+ level = { heading . props . level }
242+ text = { heading . contentText }
243+ isHighlight = { headingIdHighlight === heading . id }
244+ />
245+ </ Box >
246+ ) ,
247+ ) }
248+ </ Box >
225249 </ Box >
226250 ) ;
227251} ;
0 commit comments