1
1
import React , { useState , useRef , useEffect , useCallback } from 'react' ;
2
2
import { H6 } from '@teambit/documenter.ui.heading' ;
3
- import { CodeEditor } from '@teambit/code.ui.code -editor' ;
3
+ import Editor from '@monaco -editor/react ' ;
4
4
import { useLocation } from '@teambit/base-react.navigation.link' ;
5
5
import { defaultCodeEditorOptions } from '@teambit/api-reference.utils.code-editor-options' ;
6
6
import classnames from 'classnames' ;
@@ -10,8 +10,6 @@ import { APIRefQueryParams } from '@teambit/api-reference.hooks.use-api-ref-url'
10
10
import { useNavigate } from 'react-router-dom' ;
11
11
import { APINode } from '@teambit/api-reference.models.api-reference-model' ;
12
12
import { SchemaNodesIndex } from '@teambit/api-reference.renderers.schema-nodes-index' ;
13
- import { OnMount , Monaco } from '@monaco-editor/react' ;
14
- import * as monaco from 'monaco-editor/esm/vs/editor/editor.api' ;
15
13
16
14
import styles from './api-node-details.module.scss' ;
17
15
@@ -44,41 +42,33 @@ export function APINodeDetails({
44
42
const routerLocation = useLocation ( ) ;
45
43
const query = useQuery ( ) ;
46
44
const navigate = useNavigate ( ) ;
47
-
48
- const signatureEditorRef = useRef < monaco . editor . IStandaloneCodeEditor > ( ) ;
49
- const signatureMonacoRef = useRef < Monaco > ( ) ;
50
-
51
- const exampleEditorRef = useRef < monaco . editor . IStandaloneCodeEditor > ( ) ;
52
- const exampleMonacoRef = useRef < Monaco > ( ) ;
53
-
45
+ const editorRef = useRef < any > ( ) ;
46
+ const monacoRef = useRef < any > ( ) ;
54
47
const routeToAPICmdId = useRef < string | null > ( null ) ;
55
48
const apiUrlToRoute = useRef < string | null > ( null ) ;
56
-
57
49
const hoverProviderDispose = useRef < any > ( ) ;
58
-
59
50
const rootRef = useRef ( ) as React . MutableRefObject < HTMLDivElement > ;
60
51
const apiRef = useRef < HTMLDivElement | null > ( null ) ;
61
-
62
- const signatureContainerRef = useRef < HTMLDivElement | null > ( null ) ;
63
- const exampleContainerRef = useRef < HTMLDivElement | null > ( null ) ;
64
-
65
- const [ signatureHeight , setSignatureHeight ] = useState < string | undefined > ( ) ;
66
- const [ exampleHeight , setExampleHeight ] = useState < string | undefined > ( ) ;
67
-
68
- const [ containerSize ] = useState < { width ?: number ; height ?: number } > ( {
52
+ const currentQueryParams = query . toString ( ) ;
53
+ const [ containerSize , setContainerSize ] = useState < { width ?: number ; height ?: number } > ( {
69
54
width : undefined ,
70
55
height : undefined ,
71
56
} ) ;
72
-
73
- const currentQueryParams = query . toString ( ) ;
74
- const signatureHeightStyle = ( ! ! signatureHeight && `calc(${ signatureHeight } + 16px)` ) || '250px' ;
75
- const exampleHeightStyle = ( ! ! exampleHeight && `calc(${ exampleHeight } + 16px)` ) || '250px' ;
76
-
77
57
const indexHidden = ( containerSize . width ?? 0 ) < INDEX_THRESHOLD_WIDTH ;
78
58
79
59
const example = ( doc ?. tags || [ ] ) . find ( ( tag ) => tag . tagName === 'example' ) ;
80
60
const comment = doc ?. comment ;
81
61
const signature = displaySignature || defaultSignature ;
62
+ /**
63
+ * @HACK
64
+ * Make Monaco responsive
65
+ * default line height: 18px;
66
+ * totalHeight: (no of lines * default line height)
67
+ */
68
+ const exampleHeight = ( example ?. comment ?. split ( '\n' ) . length || 0 ) * 18 ;
69
+ const defaultSignatureHeight = 36 + ( ( signature ?. split ( '\n' ) . length || 0 ) - 1 ) * 18 ;
70
+
71
+ const [ signatureHeight , setSignatureHeight ] = useState < number > ( defaultSignatureHeight ) ;
82
72
const [ isMounted , setIsMounted ] = useState ( false ) ;
83
73
84
74
const getAPINodeUrl = useCallback ( ( queryParams : APIRefQueryParams ) => {
@@ -107,158 +97,54 @@ export function APINodeDetails({
107
97
108
98
useEffect ( ( ) => {
109
99
if ( isMounted && signature ) {
110
- signatureMonacoRef . current ? .languages . typescript . typescriptDefaults . setCompilerOptions ( {
111
- jsx : signatureMonacoRef . current . languages . typescript . JsxEmit . Preserve ,
112
- target : signatureMonacoRef . current . languages . typescript . ScriptTarget . ES2020 ,
100
+ monacoRef . current . languages . typescript . typescriptDefaults . setCompilerOptions ( {
101
+ jsx : monacoRef . current . languages . typescript . JsxEmit . Preserve ,
102
+ target : monacoRef . current . languages . typescript . ScriptTarget . ES2020 ,
113
103
esModuleInterop : true ,
114
104
} ) ;
115
- signatureMonacoRef . current ?. languages . typescript . typescriptDefaults . setDiagnosticsOptions ( {
105
+ `` ;
106
+ monacoRef . current . languages . typescript . typescriptDefaults . setDiagnosticsOptions ( {
116
107
noSemanticValidation : true ,
117
108
noSyntaxValidation : true ,
118
109
} ) ;
119
-
120
- routeToAPICmdId . current =
121
- signatureEditorRef . current ?. addCommand ( 0 , ( ) => {
122
- apiUrlToRoute . current && navigate ( apiUrlToRoute . current ) ;
123
- } ) ?? null ;
124
-
110
+ const container = editorRef . current . getDomNode ( ) ;
111
+ editorRef . current . onDidContentSizeChange ( ( { contentHeight } ) => {
112
+ if ( container && isMounted && signature ) {
113
+ const updatedHeight = Math . min ( 200 , contentHeight + 18 ) ;
114
+ setSignatureHeight ( updatedHeight ) ;
115
+ }
116
+ } ) ;
117
+ routeToAPICmdId . current = editorRef . current . addCommand ( 0 , ( ) => {
118
+ apiUrlToRoute . current && navigate ( apiUrlToRoute . current ) ;
119
+ } ) ;
125
120
if ( ! hoverProviderDispose . current ) {
126
- hoverProviderDispose . current = signatureMonacoRef . current ? .languages . registerHoverProvider ( 'typescript' , {
121
+ hoverProviderDispose . current = monacoRef . current . languages . registerHoverProvider ( 'typescript' , {
127
122
provideHover : hoverProvider ,
128
123
} ) ;
129
124
}
130
125
}
131
126
} , [ isMounted ] ) ;
132
127
133
- const getDisplayedLineCount = ( editorInstance , containerWidth ) => {
134
- if ( ! signatureMonacoRef . current ) return 0 ;
135
-
136
- const model = editorInstance . getModel ( ) ;
137
-
138
- if ( ! model ) {
139
- return 0 ;
140
- }
141
-
142
- const lineCount = model . getLineCount ( ) ;
143
-
144
- let displayedLines = 0 ;
145
-
146
- const lineWidth = editorInstance . getOption ( signatureMonacoRef . current . editor . EditorOption . wordWrapColumn ) ;
147
- const fontWidthApproximation = 8 ;
148
-
149
- for ( let lineNumber = 1 ; lineNumber <= lineCount ; lineNumber += 1 ) {
150
- const line = model . getLineContent ( lineNumber ) ;
151
- const length = line . length || 1 ;
152
- const lineFitsContainer = length * fontWidthApproximation <= containerWidth ;
153
- const wrappedLineCount = ( lineFitsContainer ? 1 : Math . ceil ( length / lineWidth ) ) || 1 ;
154
- displayedLines += wrappedLineCount ;
155
- }
156
-
157
- return displayedLines ;
158
- } ;
159
-
160
- const updateEditorHeight =
161
- (
162
- setHeight : React . Dispatch < React . SetStateAction < string | undefined > > ,
163
- editorRef : React . MutableRefObject < monaco . editor . IStandaloneCodeEditor | undefined >
164
- ) =>
165
- ( ) => {
166
- if ( ! signatureMonacoRef . current ) return undefined ;
167
-
168
- const editor = editorRef . current ;
169
-
170
- if ( ! editor ) {
171
- return undefined ;
172
- }
173
-
174
- const lineHeight = editor . getOption ( signatureMonacoRef . current . editor . EditorOption . lineHeight ) ;
175
-
176
- const paddingTop = editor . getOption ( signatureMonacoRef . current . editor . EditorOption . padding ) ?. top || 0 ;
177
- const paddingBottom = editor . getOption ( signatureMonacoRef . current . editor . EditorOption . padding ) ?. bottom || 0 ;
178
- const glyphMargin = editor . getOption ( signatureMonacoRef . current . editor . EditorOption . glyphMargin ) ;
179
- const lineNumbers = editor . getOption ( signatureMonacoRef . current . editor . EditorOption . lineNumbers ) ;
180
-
181
- const glyphMarginHeight = glyphMargin ? lineHeight : 0 ;
182
- const lineNumbersHeight = lineNumbers . renderType !== 0 ? lineHeight : 0 ;
183
-
184
- const containerWidth = editor . getLayoutInfo ( ) . contentWidth ;
185
- const displayedLines = getDisplayedLineCount ( editor , containerWidth ) ;
186
-
187
- const contentHeight =
188
- displayedLines * lineHeight + paddingTop + paddingBottom + glyphMarginHeight + lineNumbersHeight ;
189
-
190
- const domNode = editor . getDomNode ( ) ?. parentElement ;
191
-
192
- if ( ! domNode ) {
193
- return undefined ;
194
- }
195
-
196
- domNode . style . height = `${ contentHeight } px` ;
197
- signatureEditorRef . current ?. layout ( ) ;
198
- setHeight ( ( ) => `${ contentHeight } px` ) ;
199
- return undefined ;
200
- } ;
201
-
202
- const handleEditorDidMount : (
203
- monacoRef : React . MutableRefObject < Monaco | undefined > ,
204
- editorRef : React . MutableRefObject < monaco . editor . IStandaloneCodeEditor | undefined > ,
205
- containerRef : React . MutableRefObject < HTMLDivElement | null > ,
206
- setHeight : React . Dispatch < React . SetStateAction < string | undefined > >
207
- ) => OnMount = ( monacoRef , editorRef , containerRef , setHeight ) => ( editor , _monaco ) => {
208
- /**
209
- * disable syntax check
210
- * ts cant validate all types because imported files aren't available to the editor
211
- */
212
- monacoRef . current = _monaco ;
213
- editorRef . current = editor ;
214
-
215
- monacoRef . current . languages ?. typescript ?. typescriptDefaults ?. setDiagnosticsOptions ( {
216
- noSemanticValidation : true ,
217
- noSyntaxValidation : true ,
218
- } ) ;
219
-
220
- monaco . editor . defineTheme ( 'bit' , {
221
- base : 'vs-dark' ,
222
- inherit : true ,
223
- rules : [ ] ,
224
- colors : {
225
- 'scrollbar.shadow' : '#222222' ,
226
- 'diffEditor.insertedTextBackground' : '#1C4D2D' ,
227
- 'diffEditor.removedTextBackground' : '#761E24' ,
228
- 'editor.selectionBackground' : '#5A5A5A' ,
229
- 'editor.overviewRulerBorder' : '#6a57fd' ,
230
- 'editor.lineHighlightBorder' : '#6a57fd' ,
231
- } ,
128
+ const handleSize = useCallback ( ( ) => {
129
+ setContainerSize ( {
130
+ width : rootRef . current . offsetWidth ,
131
+ height : rootRef . current . offsetHeight ,
232
132
} ) ;
233
- monaco . editor . setTheme ( 'bit' ) ;
234
- editor . onDidChangeModelDecorations ( updateEditorHeight ( setHeight , editorRef ) ) ;
235
- editor . onDidChangeModelDecorations ( updateEditorHeight ( setHeight , editorRef ) ) ;
236
- const containerElement = containerRef . current ;
237
- let resizeObserver : ResizeObserver | undefined ;
238
-
239
- if ( containerElement ) {
240
- resizeObserver = new ResizeObserver ( ( ) => {
241
- setTimeout ( ( ) => updateEditorHeight ( setHeight , editorRef ) ) ;
242
- } ) ;
243
- resizeObserver . observe ( containerElement ) ;
244
- }
245
-
246
- return ( ) => containerElement && resizeObserver ?. unobserve ( containerElement ) ;
247
- } ;
133
+ } , [ ] ) ;
248
134
249
135
useEffect ( ( ) => {
250
- updateEditorHeight ( setSignatureHeight , signatureEditorRef ) ( ) ;
251
- updateEditorHeight ( setExampleHeight , exampleEditorRef ) ( ) ;
252
-
136
+ if ( window ) window . addEventListener ( 'resize' , handleSize ) ;
137
+ // Call handler right away so state gets updated with initial container size
138
+ handleSize ( ) ;
253
139
return ( ) => {
254
140
hoverProviderDispose . current ?. dispose ( ) ;
141
+ if ( window ) window . removeEventListener ( 'resize' , handleSize ) ;
255
142
setIsMounted ( false ) ;
256
143
} ;
257
144
} , [ ] ) ;
258
145
259
- React . useLayoutEffect ( ( ) => {
260
- updateEditorHeight ( setSignatureHeight , signatureEditorRef ) ( ) ;
261
- updateEditorHeight ( setExampleHeight , exampleEditorRef ) ( ) ;
146
+ useEffect ( ( ) => {
147
+ handleSize ( ) ;
262
148
} , [ rootRef ?. current ?. offsetHeight , rootRef ?. current ?. offsetWidth ] ) ;
263
149
264
150
return (
@@ -278,53 +164,38 @@ export function APINodeDetails({
278
164
< div
279
165
key = { `${ signature } -${ currentQueryParams } -api-signature-editor` }
280
166
className = { classnames ( styles . apiNodeDetailsSignatureContainer , styles . codeEditorContainer ) }
281
- ref = { signatureContainerRef }
282
- style = { {
283
- minHeight : signatureHeightStyle ,
284
- maxHeight : signatureHeightStyle ,
285
- height : signatureHeightStyle ,
286
- } }
287
167
>
288
- < CodeEditor
168
+ < Editor
289
169
options = { defaultCodeEditorOptions }
290
- fileContent = { signature }
291
- filePath = { `${ currentQueryParams } -${ filePath } ` }
170
+ value = { signature }
171
+ height = { signatureHeight }
172
+ path = { `${ currentQueryParams } -${ filePath } ` }
292
173
className = { styles . editor }
293
- handleEditorBeforeMount = { ( _monaco ) => {
294
- signatureMonacoRef . current = _monaco ;
174
+ beforeMount = { ( monaco ) => {
175
+ monacoRef . current = monaco ;
176
+ } }
177
+ onMount = { ( editor ) => {
178
+ editorRef . current = editor ;
179
+ const signatureContent = editorRef . current . getValue ( ) ;
180
+ const updatedSignatureHeight = 36 + ( ( signatureContent ?. split ( '\n' ) . length || 0 ) - 1 ) * 18 ;
181
+ setIsMounted ( true ) ;
182
+ setSignatureHeight ( updatedSignatureHeight ) ;
295
183
} }
296
- handleEditorDidMount = { handleEditorDidMount (
297
- signatureMonacoRef ,
298
- signatureEditorRef ,
299
- signatureContainerRef ,
300
- setSignatureHeight
301
- ) }
184
+ theme = { 'vs-dark' }
302
185
/>
303
186
</ div >
304
187
) }
305
188
{ example && example . comment && (
306
189
< div className = { styles . apiNodeDetailsExample } >
307
190
< H6 className = { styles . apiNodeDetailsExampleTitle } > Example</ H6 >
308
- < div
309
- className = { styles . codeEditorContainer }
310
- ref = { exampleContainerRef }
311
- style = { {
312
- minHeight : exampleHeightStyle ,
313
- maxHeight : exampleHeightStyle ,
314
- height : exampleHeightStyle ,
315
- } }
316
- >
317
- < CodeEditor
191
+ < div className = { styles . codeEditorContainer } >
192
+ < Editor
318
193
options = { defaultCodeEditorOptions }
319
- fileContent = { example . comment }
320
- filePath = { `${ example ?. location . line } :${ example ?. location . filePath } ` }
194
+ value = { example . comment }
195
+ path = { `${ example ?. location . line } :${ example ?. location . filePath } ` }
196
+ height = { exampleHeight }
197
+ theme = { 'vs-dark' }
321
198
className = { styles . editor }
322
- handleEditorDidMount = { handleEditorDidMount (
323
- exampleMonacoRef ,
324
- exampleEditorRef ,
325
- exampleContainerRef ,
326
- setExampleHeight
327
- ) }
328
199
/>
329
200
</ div >
330
201
</ div >
0 commit comments