33 * SPDX-License-Identifier: Apache-2.0
44 */
55
6- import React , { useMemo , useRef } from "react"
7- import CodeMirror , { EditorView , highlightWhitespace } from "@uiw/react-codemirror"
6+ import React , { useMemo , useRef , useEffect } from "react"
7+ import { EditorView , highlightWhitespace , lineNumbers } from "@codemirror/view"
8+ import { EditorState } from "@codemirror/state"
89import { yaml } from "@codemirror/lang-yaml"
10+ import { oneDark } from "@codemirror/theme-one-dark"
911import yamlParser from "js-yaml"
1012import { ErrorMessage } from "../common/ErrorBoundary/ErrorMessage"
1113
12- interface YamlViewerProps extends Omit < React . ComponentProps < typeof CodeMirror > , "value" > {
14+ interface YamlViewerProps extends Omit < React . HTMLAttributes < HTMLDivElement > , "value" > {
1315 value : object
16+ className ?: string
1417}
1518
16- export default function YamlViewer ( { value, ...props } : YamlViewerProps ) {
19+ function createEditorExtensions ( ) {
20+ return [
21+ yaml ( ) ,
22+ oneDark ,
23+ highlightWhitespace ( ) ,
24+ lineNumbers ( ) ,
25+ EditorView . editable . of ( false ) ,
26+ EditorView . lineWrapping ,
27+ EditorView . theme ( {
28+ ".cm-highlightSpace" : {
29+ backgroundImage :
30+ "url(\"data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='6' height='6'><circle cx='3' cy='3' r='1' fill='%23cccccc' /></svg>\")" ,
31+ backgroundRepeat : "no-repeat" ,
32+ backgroundPosition : "center" ,
33+ backgroundSize : "contain" ,
34+ opacity : 0.1 ,
35+ } ,
36+ ".cm-scroller" : {
37+ fontFamily : "monospace" ,
38+ } ,
39+ } ) ,
40+ EditorView . contentAttributes . of ( {
41+ "aria-label" : "YAML data viewer (read-only)" ,
42+ "aria-readonly" : "true" ,
43+ } ) ,
44+ ]
45+ }
46+
47+ export default function YamlViewer ( { value, className = "" , ...props } : YamlViewerProps ) {
1748 const containerRef = useRef < HTMLDivElement > ( null )
49+ const editorViewRef = useRef < EditorView | null > ( null )
1850
1951 const { yamlContent, error } = useMemo ( ( ) => {
2052 try {
@@ -33,34 +65,54 @@ export default function YamlViewer({ value, ...props }: YamlViewerProps) {
3365 }
3466 } , [ value ] )
3567
68+ // Store initial content in a ref to avoid triggering effect re-runs
69+ const initialContentRef = useRef ( yamlContent )
70+
71+ // Create the CodeMirror editor instance once
72+ useEffect ( ( ) => {
73+ if ( ! containerRef . current ) return
74+
75+ const state = EditorState . create ( {
76+ doc : initialContentRef . current ,
77+ extensions : createEditorExtensions ( ) ,
78+ } )
79+
80+ const view = new EditorView ( {
81+ state,
82+ parent : containerRef . current ,
83+ } )
84+
85+ editorViewRef . current = view
86+
87+ return ( ) => {
88+ view . destroy ( )
89+ editorViewRef . current = null
90+ }
91+ } , [ ] )
92+
93+ // Update editor content when yamlContent changes
94+ useEffect ( ( ) => {
95+ if ( ! editorViewRef . current ) return
96+
97+ const currentDoc = editorViewRef . current . state . doc . toString ( )
98+ if ( currentDoc !== yamlContent ) {
99+ const scrollPos = editorViewRef . current . scrollDOM . scrollTop
100+
101+ editorViewRef . current . dispatch ( {
102+ changes : {
103+ from : 0 ,
104+ to : editorViewRef . current . state . doc . length ,
105+ insert : yamlContent ,
106+ } ,
107+ } )
108+
109+ editorViewRef . current . scrollDOM . scrollTop = scrollPos
110+ }
111+ } , [ yamlContent ] )
112+
36113 return (
37- < div ref = { containerRef } className = "overflow-x-auto max-w-full" >
38- { error ? (
39- < ErrorMessage error = { new Error ( error ) } />
40- ) : (
41- < CodeMirror
42- value = { yamlContent }
43- theme = "dark"
44- extensions = { [
45- yaml ( ) ,
46- highlightWhitespace ( ) ,
47- EditorView . theme ( {
48- ".cm-highlightSpace" : {
49- backgroundImage :
50- "url(\"data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='6' height='6'><circle cx='3' cy='3' r='1' fill='%23cccccc' /></svg>\")" ,
51- backgroundRepeat : "no-repeat" ,
52- backgroundPosition : "center" ,
53- backgroundSize : "contain" ,
54- opacity : 0.1 ,
55- } ,
56- } ) ,
57- ] }
58- editable = { false }
59- aria-label = "YAML data viewer (read-only)"
60- aria-readonly = "true"
61- { ...props }
62- />
63- ) }
114+ < div className = { `overflow-x-auto max-w-full ${ className } ` } { ...props } >
115+ { error ? < ErrorMessage error = { error } /> : < div ref = { containerRef } /> }
64116 </ div >
65117 )
66118}
0 commit comments