1
- import 'split-pane-react/esm/themes/default.css'
1
+ import 'split-pane-react/esm/themes/default.css' ;
2
2
3
- import { useEffect , useState , useRef } from 'react'
4
- import { useRecoilState } from 'recoil'
5
- import { Pane } from 'split-pane-react'
6
- import { Decimal } from 'internet-object'
3
+ import { Decimal } from 'internet-object' ;
4
+ import { useCallback , useEffect , useRef , useState } from 'react' ;
5
+ import Toggle from 'react-toggle' ;
6
+ import { useRecoilState } from 'recoil' ;
7
+ import { Pane } from 'split-pane-react' ;
8
+ import SplitPane from 'split-pane-react/esm/SplitPane' ;
7
9
8
- import Toggle from 'react-toggle'
9
- import SplitPane from 'split-pane-react/esm/SplitPane'
10
-
11
- import parseIO from './compiler'
12
- import Bar from '../../components/bar/Bar'
13
- import Editor from '../../components/editor/Editor'
14
- import Output from '../../components/output/Output'
15
- import editorPosition from '../../states/editor-pos'
10
+ import Bar from '../../components/bar/Bar' ;
11
+ import Editor from '../../components/editor/Editor' ;
12
+ import Output from '../../components/output/Output' ;
13
+ import editorPosition from '../../states/editor-pos' ;
14
+ import { useParseIO , Marker } from '../../hooks/useParseIO' ;
16
15
17
16
interface PlaygroundProps {
18
17
showSchema : boolean ;
@@ -22,143 +21,122 @@ interface PlaygroundProps {
22
21
schemaPanelHeight ?: number ;
23
22
}
24
23
24
+ const DEFAULT_SCHEMA_PANEL_HEIGHT = 200 ;
25
+ const MIN_PANEL_SIZE = 100 ;
26
+ const DEFAULT_VERTICAL_SIZE = '60%' ;
27
+
25
28
const Playground = ( {
26
29
showSchema, setShowSchema,
27
30
document,
28
31
schema,
29
32
schemaPanelHeight,
30
33
} : PlaygroundProps ) => {
31
- const [ _ , setEditorPos ] = useRecoilState ( editorPosition )
32
34
33
- const [ sizesH , setHSizes ] = useState < [ number | string , string ] > ( [ 0 , "auto" ] )
34
- const sizesHRef = useRef ( sizesH )
35
- // Keep the ref in sync with state
35
+ const [ , setEditorPos ] = useRecoilState ( editorPosition ) ;
36
+
37
+ // State for horizontal and vertical split sizes
38
+ const [ sizesH , setHSizes ] = useState < [ number | string , string ] > ( [ 0 , 'auto' ] ) ;
39
+ const sizesHRef = useRef ( sizesH ) ;
36
40
useEffect ( ( ) => {
37
- sizesHRef . current = sizesH
38
- } , [ sizesH ] )
39
-
40
- const [ sizesV , setVSizes ] = useState ( [ 0 , "auto" ] )
41
- const [ schemaText , setSchemaText ] = useState ( schema )
42
- const [ documentText , setDocumentText ] = useState ( document )
43
- const [ jsonText , setJsonText ] = useState ( "" )
44
- const [ markers , setMarkers ] = useState < any [ ] > ( [ ] )
45
- const [ defMarkers , setDefMarkers ] = useState < any [ ] > ( [ ] )
46
- const [ error , setError ] = useState < boolean > ( false )
47
- const [ minifiedOutput , setMinifiedOutput ] = useState (
48
- localStorage . getItem ( "minifiedOutput" ) === "true" ? true : false )
41
+ sizesHRef . current = sizesH ;
42
+ } , [ sizesH ] ) ;
49
43
44
+ const [ sizesV , setVSizes ] = useState < [ number | string , string ] > ( [ 0 , 'auto' ] ) ;
45
+ const [ schemaText , setSchemaText ] = useState < string > ( schema ) ;
46
+ const [ documentText , setDocumentText ] = useState < string > ( document ) ;
47
+ const [ jsonText , setJsonText ] = useState < string > ( '' ) ;
48
+ const [ minifiedOutput , setMinifiedOutput ] = useState < boolean > ( localStorage . getItem ( 'minifiedOutput' ) === 'true' ) ;
49
+ // Use custom hook for parsing logic and marker state
50
+ const { markers, defMarkers, jsonText : parsedJsonText , error, parse } = useParseIO ( documentText , schemaText , showSchema , minifiedOutput ) ;
51
+
52
+
53
+ // Set initial horizontal split size when schemaPanelHeight changes
50
54
useEffect ( ( ) => {
51
- setHSizes ( [ schemaPanelHeight || 200 , " auto" ] )
52
- } , [ schemaPanelHeight ] )
55
+ setHSizes ( [ schemaPanelHeight || DEFAULT_SCHEMA_PANEL_HEIGHT , ' auto' ] ) ;
56
+ } , [ schemaPanelHeight ] ) ;
53
57
58
+ // Debounced parse effect
54
59
useEffect ( ( ) => {
55
- setTimeout ( parse , 500 )
56
- } , [ schemaText , documentText , showSchema , minifiedOutput ] )
60
+ const timer = setTimeout ( ( ) => {
61
+ parse ( ) ;
62
+ } , 500 ) ;
63
+ return ( ) => clearTimeout ( timer ) ;
64
+ } , [ schemaText , documentText , showSchema , minifiedOutput , parse ] ) ;
57
65
66
+ // Keep jsonText in sync with parsedJsonText
58
67
useEffect ( ( ) => {
59
- setSchemaText ( schema )
60
- setDocumentText ( document )
61
- } , [ schema , document ] )
68
+ setJsonText ( parsedJsonText ) ;
69
+ } , [ parsedJsonText ] ) ;
62
70
71
+ // Sync schema and document text with props
72
+ useEffect ( ( ) => {
73
+ setSchemaText ( schema ) ;
74
+ setDocumentText ( document ) ;
75
+ } , [ schema , document ] ) ;
76
+
77
+ // Set initial vertical split size
63
78
useEffect ( ( ) => {
64
79
if ( sizesV [ 0 ] === 0 ) {
65
- setVSizes ( [ "60%" , " auto" ] )
80
+ setVSizes ( [ DEFAULT_VERTICAL_SIZE , ' auto' ] ) ;
66
81
}
67
- } , [ sizesV ] )
82
+ } , [ sizesV ] ) ;
68
83
84
+ // Update horizontal split when showSchema changes
69
85
useEffect ( ( ) => {
70
86
if ( ! showSchema ) {
71
- setHSizes ( [ 0 , "auto" ] )
72
- } else {
73
- setHSizes ( [ schemaPanelHeight || 200 , "auto" ] )
74
- }
75
- } , [ showSchema ] )
76
-
77
- const parse = ( ) => {
78
- const result = parseIO ( documentText , showSchema ? schemaText : null )
79
- if ( result . defsMarkers ) {
80
- setDefMarkers ( result . defsMarkers )
81
- } else {
82
- setDefMarkers ( [ ] )
83
- }
84
-
85
- if ( result . docMarkers ) {
86
- setMarkers ( result . docMarkers )
87
+ setHSizes ( [ 0 , 'auto' ] ) ;
87
88
} else {
88
- setMarkers ( [ ] )
89
+ setHSizes ( [ schemaPanelHeight || DEFAULT_SCHEMA_PANEL_HEIGHT , 'auto' ] ) ;
89
90
}
90
-
91
- if ( result . output ) {
92
- const output = JSON . stringify ( result . output , function ( k , v :any ) {
93
- // Convert BigInt to string
94
- if ( typeof v === "bigint" ) return `io:big:${ v . toString ( ) } `
95
- if ( typeof v === "number" ) {
96
- if ( isNaN ( v ) ) return "io:number:NaN"
97
- }
98
-
99
- if ( v instanceof Decimal ) {
100
- return `io:decimal:${ v . toString ( ) } `
101
- }
102
-
103
- if ( v === Infinity ) return "io:number:Inf"
104
- if ( v === - Infinity ) return "io:number:-Inf"
105
- if ( typeof v === "undefined" ) return "io:undefined"
106
-
107
- return v
108
- } , minifiedOutput ? 0 : 2 )
109
- setJsonText ( output )
110
- setError ( false )
111
- } else {
112
- setJsonText ( result . errorMessage || "" )
113
- setError ( true )
114
- }
115
- }
91
+ } , [ showSchema , schemaPanelHeight ] ) ;
116
92
117
93
const layoutCSS = { height : "100%" }
118
94
119
- const handleHBarDragEnd = ( ) : void => {
120
- const currentSizesH = sizesHRef . current
121
- if ( typeof currentSizesH [ 0 ] === "number" ) {
122
- if ( currentSizesH [ 0 ] <= 100 ) {
95
+
96
+ // Handlers with useCallback for stable references
97
+ const handleHBarDragEnd = useCallback ( ( ) : void => {
98
+ const currentSizesH = sizesHRef . current ;
99
+ if ( typeof currentSizesH [ 0 ] === 'number' ) {
100
+ if ( currentSizesH [ 0 ] <= MIN_PANEL_SIZE ) {
123
101
if ( showSchema ) {
124
- setHSizes ( [ 0 , " auto" ] )
125
- setShowSchema ( false )
102
+ setHSizes ( [ 0 , ' auto' ] ) ;
103
+ setShowSchema ( false ) ;
126
104
} else {
127
- setHSizes ( [ 200 , " auto" ] )
128
- setShowSchema ( true )
105
+ setHSizes ( [ DEFAULT_SCHEMA_PANEL_HEIGHT , ' auto' ] ) ;
106
+ setShowSchema ( true ) ;
129
107
}
130
108
} else {
131
- setShowSchema ( currentSizesH [ 0 ] > 0 )
109
+ setShowSchema ( currentSizesH [ 0 ] > 0 ) ;
132
110
}
133
111
}
134
- }
112
+ } , [ showSchema , setShowSchema ] ) ;
135
113
136
- const handleHBar = ( s : any ) : void => {
137
- setHSizes ( s )
114
+ const handleHBar = useCallback ( ( s : [ number | string , string ] ) : void => {
115
+ setHSizes ( s ) ;
138
116
// sizesHRef will be updated by the useEffect above
139
- }
117
+ } , [ ] ) ;
140
118
141
- const handleSchemaChange = ( value : string ) : void => {
142
- setSchemaText ( value )
143
- }
119
+ const handleSchemaChange = useCallback ( ( value : string ) : void => {
120
+ setSchemaText ( value ) ;
121
+ } , [ ] ) ;
144
122
145
- const handleIOChange = ( value : string ) : void => {
146
- setDocumentText ( value )
147
- }
123
+ const handleIOChange = useCallback ( ( value : string ) : void => {
124
+ setDocumentText ( value ) ;
125
+ } , [ ] ) ;
148
126
149
- const handleCaretPositionChange = ( name : string , position : any ) : void => {
127
+ const handleCaretPositionChange = useCallback ( ( name : string , position : any ) : void => {
150
128
setEditorPos ( {
151
129
editorName : name ,
152
130
row : position . row ,
153
131
column : position . column ,
154
- position : position . position
155
- } )
156
- }
132
+ position : position . position ,
133
+ } ) ;
134
+ } , [ setEditorPos ] ) ;
157
135
158
- const handleOnCompressChange = ( event : any ) : void => {
159
- localStorage . setItem ( " minifiedOutput" , event . target . checked )
160
- setMinifiedOutput ( event . target . checked )
161
- }
136
+ const handleOnCompressChange = useCallback ( ( event : React . ChangeEvent < HTMLInputElement > ) : void => {
137
+ localStorage . setItem ( ' minifiedOutput' , event . target . checked . toString ( ) ) ;
138
+ setMinifiedOutput ( event . target . checked ) ;
139
+ } , [ ] ) ;
162
140
163
141
return (
164
142
< div className = "editor-area" >
@@ -210,7 +188,11 @@ const Playground = ({
210
188
< Bar label = "JSON Output" bytes = { jsonText . length } >
211
189
< label className = "toggleSwtich" title = "Compress" >
212
190
< span > Minify</ span >
213
- < Toggle onChange = { handleOnCompressChange } checked = { minifiedOutput } />
191
+ < Toggle
192
+ onChange = { handleOnCompressChange }
193
+ checked = { minifiedOutput }
194
+ aria-label = "Toggle minified JSON output"
195
+ />
214
196
</ label >
215
197
</ Bar >
216
198
< Output value = { jsonText } options = { {
0 commit comments