1- import React , { useState , useRef , useEffect , useCallback } from 'react' ;
2- import { createPortal } from 'react-dom' ;
3- import Mermaid from '@theme/Mermaid' ;
4- import styles from './styles.module.css' ;
1+ import React , { useState , useRef , useEffect , useCallback } from 'react'
2+ import { createPortal } from 'react-dom'
3+ import Mermaid from '@theme/Mermaid'
4+ import styles from './styles.module.css'
55
66const ZoomableMermaid = ( { children, title, defaultZoom = 1.2 } ) => {
7- const [ isModalOpen , setIsModalOpen ] = useState ( false ) ;
8- const [ isHovered , setIsHovered ] = useState ( false ) ;
9- const [ zoomLevel , setZoomLevel ] = useState ( defaultZoom ) ; // Use defaultZoom prop
10- const modalRef = useRef ( null ) ;
11- const containerRef = useRef ( null ) ;
7+ const [ isModalOpen , setIsModalOpen ] = useState ( false )
8+ const [ isHovered , setIsHovered ] = useState ( false )
9+ const [ zoomLevel , setZoomLevel ] = useState ( defaultZoom ) // Use defaultZoom prop
10+ const modalRef = useRef ( null )
11+ const containerRef = useRef ( null )
1212
1313 const openModal = useCallback ( ( ) => {
14- setIsModalOpen ( true ) ;
15- setZoomLevel ( defaultZoom ) ; // Reset to default zoom when opening
16- document . body . style . overflow = 'hidden' ;
17- } , [ defaultZoom ] ) ;
14+ setIsModalOpen ( true )
15+ setZoomLevel ( defaultZoom ) // Reset to default zoom when opening
16+ document . body . style . overflow = 'hidden'
17+ } , [ defaultZoom ] )
1818
1919 const closeModal = useCallback ( ( ) => {
20- setIsModalOpen ( false ) ;
21- document . body . style . overflow = 'unset' ;
20+ setIsModalOpen ( false )
21+ document . body . style . overflow = 'unset'
2222 // Return focus to the original container
2323 if ( containerRef . current ) {
24- containerRef . current . focus ( ) ;
24+ containerRef . current . focus ( )
2525 }
26- } , [ ] ) ;
26+ } , [ ] )
2727
2828 const zoomIn = useCallback ( ( ) => {
29- setZoomLevel ( prev => Math . min ( prev + 0.2 , 3.0 ) ) ; // Max 300%
30- } , [ ] ) ;
29+ setZoomLevel ( prev => Math . min ( prev + 0.2 , 3.0 ) ) // Max 300%
30+ } , [ ] )
3131
3232 const zoomOut = useCallback ( ( ) => {
33- setZoomLevel ( prev => Math . max ( prev - 0.2 , 0.5 ) ) ; // Min 50%
34- } , [ ] ) ;
33+ setZoomLevel ( prev => Math . max ( prev - 0.2 , 0.5 ) ) // Min 50%
34+ } , [ ] )
3535
3636 const resetZoom = useCallback ( ( ) => {
37- setZoomLevel ( defaultZoom ) ; // Reset to custom default instead of hardcoded 1.2
38- } , [ defaultZoom ] ) ;
37+ setZoomLevel ( defaultZoom ) // Reset to custom default instead of hardcoded 1.2
38+ } , [ defaultZoom ] )
3939
4040 useEffect ( ( ) => {
4141 const handleEscape = ( e ) => {
4242 if ( e . key === 'Escape' && isModalOpen ) {
43- closeModal ( ) ;
43+ closeModal ( )
4444 }
45- } ;
45+ }
4646
4747 const handleClickOutside = ( e ) => {
4848 if ( modalRef . current && ! modalRef . current . contains ( e . target ) ) {
49- closeModal ( ) ;
49+ closeModal ( )
5050 }
51- } ;
51+ }
5252
5353 const handleKeydown = ( e ) => {
54- if ( ! isModalOpen ) return ;
55-
54+ if ( ! isModalOpen ) return
55+
5656 if ( e . key === '=' || e . key === '+' ) {
57- e . preventDefault ( ) ;
58- zoomIn ( ) ;
59- } else if ( e . key === '-' ) {
60- e . preventDefault ( ) ;
61- zoomOut ( ) ;
62- } else if ( e . key === '0' ) {
63- e . preventDefault ( ) ;
64- resetZoom ( ) ;
57+ e . preventDefault ( )
58+ zoomIn ( )
59+ }
60+ else if ( e . key === '-' ) {
61+ e . preventDefault ( )
62+ zoomOut ( )
63+ }
64+ else if ( e . key === '0' ) {
65+ e . preventDefault ( )
66+ resetZoom ( )
6567 }
66- } ;
68+ }
6769
6870 if ( isModalOpen ) {
69- document . addEventListener ( 'keydown' , handleEscape ) ;
70- document . addEventListener ( 'mousedown' , handleClickOutside ) ;
71- document . addEventListener ( 'keydown' , handleKeydown ) ;
72-
71+ document . addEventListener ( 'keydown' , handleEscape )
72+ document . addEventListener ( 'mousedown' , handleClickOutside )
73+ document . addEventListener ( 'keydown' , handleKeydown )
74+
7375 // Focus the modal content when opened
7476 setTimeout ( ( ) => {
7577 if ( modalRef . current ) {
76- modalRef . current . focus ( ) ;
78+ modalRef . current . focus ( )
7779 }
78- } , 100 ) ;
80+ } , 100 )
7981 }
8082
8183 return ( ) => {
82- document . removeEventListener ( 'keydown' , handleEscape ) ;
83- document . removeEventListener ( 'mousedown' , handleClickOutside ) ;
84- document . removeEventListener ( 'keydown' , handleKeydown ) ;
85- } ;
86- } , [ isModalOpen , closeModal , zoomIn , zoomOut , resetZoom ] ) ;
84+ document . removeEventListener ( 'keydown' , handleEscape )
85+ document . removeEventListener ( 'mousedown' , handleClickOutside )
86+ document . removeEventListener ( 'keydown' , handleKeydown )
87+ }
88+ } , [ isModalOpen , closeModal , zoomIn , zoomOut , resetZoom ] )
8789
8890 // Cleanup on unmount
8991 useEffect ( ( ) => {
9092 return ( ) => {
91- document . body . style . overflow = 'unset' ;
92- } ;
93- } , [ ] ) ;
93+ document . body . style . overflow = 'unset'
94+ }
95+ } , [ ] )
9496
9597 const handleKeyDown = ( e ) => {
9698 if ( e . key === 'Enter' || e . key === ' ' ) {
97- e . preventDefault ( ) ;
98- openModal ( ) ;
99+ e . preventDefault ( )
100+ openModal ( )
99101 }
100- } ;
102+ }
101103
102104 const modalContent = (
103- < div
105+ < div
104106 className = { styles . modal }
105107 role = "dialog"
106108 aria-modal = "true"
107- aria-labelledby = { title ? " modal-title" : undefined }
109+ aria-labelledby = { title ? ' modal-title' : undefined }
108110 aria-describedby = "modal-description"
109111 >
110- < div
112+ < div
111113 className = { styles . modalContent }
112114 ref = { modalRef }
113115 tabIndex = { - 1 }
@@ -120,9 +122,10 @@ const ZoomableMermaid = ({ children, title, defaultZoom = 1.2 }) => {
120122 ) }
121123 < div className = { styles . modalControls } >
122124 < span className = { styles . zoomIndicator } >
123- { Math . round ( zoomLevel * 100 ) } %
125+ { Math . round ( zoomLevel * 100 ) }
126+ %
124127 </ span >
125- < button
128+ < button
126129 className = { styles . zoomButton }
127130 onClick = { zoomOut }
128131 disabled = { zoomLevel <= 0.5 }
@@ -131,25 +134,25 @@ const ZoomableMermaid = ({ children, title, defaultZoom = 1.2 }) => {
131134 title = "缩小 (快捷键: -)"
132135 >
133136 < svg width = "18" height = "18" viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" strokeWidth = "2" >
134- < circle cx = "11" cy = "11" r = "8" />
135- < path d = "M8 11h6" />
136- < path d = "m21 21-4.35-4.35" />
137+ < circle cx = "11" cy = "11" r = "8" />
138+ < path d = "M8 11h6" />
139+ < path d = "m21 21-4.35-4.35" />
137140 </ svg >
138141 </ button >
139- < button
142+ < button
140143 className = { styles . resetButton }
141144 onClick = { resetZoom }
142145 aria-label = { `重置到默认缩放 ${ Math . round ( defaultZoom * 100 ) } %` }
143146 type = "button"
144147 title = { `重置到默认缩放 ${ Math . round ( defaultZoom * 100 ) } % (快捷键: 0)` }
145148 >
146149 < svg width = "18" height = "18" viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" strokeWidth = "2" >
147- < path d = "M3 3l18 18" />
148- < path d = "m19 4-7 7-7-7" />
149- < path d = "m5 20 7-7 7 7" />
150+ < path d = "M3 3l18 18" />
151+ < path d = "m19 4-7 7-7-7" />
152+ < path d = "m5 20 7-7 7 7" />
150153 </ svg >
151154 </ button >
152- < button
155+ < button
153156 className = { styles . zoomButton }
154157 onClick = { zoomIn }
155158 disabled = { zoomLevel >= 3.0 }
@@ -158,31 +161,31 @@ const ZoomableMermaid = ({ children, title, defaultZoom = 1.2 }) => {
158161 title = "放大 (快捷键: +)"
159162 >
160163 < svg width = "18" height = "18" viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" strokeWidth = "2" >
161- < circle cx = "11" cy = "11" r = "8" />
162- < path d = "M8 11h6" />
163- < path d = "M11 8v6" />
164- < path d = "m21 21-4.35-4.35" />
164+ < circle cx = "11" cy = "11" r = "8" />
165+ < path d = "M8 11h6" />
166+ < path d = "M11 8v6" />
167+ < path d = "m21 21-4.35-4.35" />
165168 </ svg >
166169 </ button >
167- < button
170+ < button
168171 className = { styles . closeButton }
169172 onClick = { closeModal }
170173 aria-label = "关闭放大视图"
171174 type = "button"
172175 >
173176 < svg width = "18" height = "18" viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" strokeWidth = "2" >
174- < line x1 = "18" y1 = "6" x2 = "6" y2 = "18" />
175- < line x1 = "6" y1 = "6" x2 = "18" y2 = "18" />
177+ < line x1 = "18" y1 = "6" x2 = "6" y2 = "18" />
178+ < line x1 = "6" y1 = "6" x2 = "18" y2 = "18" />
176179 </ svg >
177180 </ button >
178181 </ div >
179182 </ div >
180- < div
183+ < div
181184 className = { styles . modalBody }
182185 id = "modal-description"
183186 aria-label = "放大的 Mermaid 图表"
184187 >
185- < div
188+ < div
186189 className = { styles . diagramContainer }
187190 style = { { transform : `scale(${ zoomLevel } )` , transformOrigin : 'center' } }
188191 >
@@ -191,11 +194,11 @@ const ZoomableMermaid = ({ children, title, defaultZoom = 1.2 }) => {
191194 </ div >
192195 </ div >
193196 </ div >
194- ) ;
197+ )
195198
196199 return (
197200 < >
198- < div
201+ < div
199202 ref = { containerRef }
200203 className = { `${ styles . mermaidContainer } ${ isHovered ? styles . hovered : '' } ` }
201204 onClick = { openModal }
@@ -209,10 +212,10 @@ const ZoomableMermaid = ({ children, title, defaultZoom = 1.2 }) => {
209212 >
210213 < div className = { styles . zoomHint } aria-hidden = "true" >
211214 < svg width = "20" height = "20" viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" strokeWidth = "2" >
212- < circle cx = "11" cy = "11" r = "8" />
213- < path d = "m21 21-4.35-4.35" />
214- < path d = "M11 8v6" />
215- < path d = "M8 11h6" />
215+ < circle cx = "11" cy = "11" r = "8" />
216+ < path d = "m21 21-4.35-4.35" />
217+ < path d = "M11 8v6" />
218+ < path d = "M8 11h6" />
216219 </ svg >
217220 < span > 点击放大</ span >
218221 </ div >
@@ -221,7 +224,7 @@ const ZoomableMermaid = ({ children, title, defaultZoom = 1.2 }) => {
221224
222225 { isModalOpen && createPortal ( modalContent , document . body ) }
223226 </ >
224- ) ;
225- } ;
227+ )
228+ }
226229
227- export default ZoomableMermaid ;
230+ export default ZoomableMermaid
0 commit comments