1- import React , { useState } from 'react' ;
1+ import React , { useState , useEffect , useRef } from 'react' ;
22
33import { BrowserRouter as Router , Route } from 'react-router-dom' ;
44
55import MagicDropzone from 'react-magic-dropzone' ;
66// import Editor from 'react-monaco-editor';
77import Editor , { monaco } from '@monaco-editor/react' ;
88
9+ import SwaggerParser from '@apidevtools/swagger-parser' ;
10+
911import queryString from 'query-string' ;
1012
1113import DocSidebar from './DocSidebar' ;
@@ -14,6 +16,8 @@ import styles from './App.module.css';
1416
1517import spec from './spec.json' ;
1618
19+ import { sampleFromSchema } from './x-utils' ;
20+
1721import './default-dark.css' ;
1822
1923monaco
@@ -25,10 +29,10 @@ monaco
2529 inherit : false ,
2630 rules : [
2731 { token : '' , foreground : '7f7f7f' } ,
28- // { token: 'string.key.json', foreground: 'd4d4d4' },
2932 { token : 'string.key.json' , foreground : 'f5f6f7' } ,
3033 { token : 'string.value.json' , foreground : '85d996' } ,
3134 { token : 'number' , foreground : 'a4cdfe' } ,
35+ { token : 'keyword.json' , foreground : 'a4cdfe' } ,
3236 ] ,
3337 colors : {
3438 // 'editor.background': '#393939',
@@ -127,28 +131,74 @@ function organizeSpec(spec) {
127131 } ) ;
128132}
129133
130- function Curl ( { item, path, query, header, cookie, accept } ) {
131- const multiline = accept !== undefined ;
132-
134+ function Curl ( {
135+ theref,
136+ item,
137+ path,
138+ query,
139+ header,
140+ cookie,
141+ accept,
142+ body,
143+ contentType,
144+ } ) {
133145 const qs = queryString . stringify ( query ) ;
134146
147+ let bodyString ;
148+ try {
149+ bodyString = JSON . stringify ( JSON . stringify ( JSON . parse ( body ) ) ) ;
150+ } catch {
151+ bodyString = '"{}"' ;
152+ }
153+
135154 return (
136- < >
137- < div >
155+ < code ref = { theref } className = { styles . curlything } >
156+ < span >
138157 curl -X < span > { item . method . toUpperCase ( ) } </ span > "
139158 { window . location . origin }
140159 { item . path . replace ( / { ( [ a - z 0 - 9 - _ ] + ) } / gi, ( _ , p1 ) => {
141160 return path [ p1 ] || `:${ p1 } ` ;
142161 } ) }
143162 { qs && '?' }
144- { qs } " { multiline && '\\' }
145- </ div >
163+ { qs } "
164+ </ span >
165+
146166 { accept && (
147- < div >
148- { ' ' } -H < span style = { { color : '#85d996' } } > "accept: { accept } "</ span >
149- </ div >
167+ < >
168+ { ' \\' }
169+ < br />
170+ < span >
171+ { ' ' }
172+ -H < span style = { { color : '#85d996' } } > "Accept: { accept } "</ span >
173+ </ span >
174+ </ >
150175 ) }
151- </ >
176+
177+ { contentType && (
178+ < >
179+ { ' \\' }
180+ < br />
181+ < span >
182+ { ' ' }
183+ -H{ ' ' }
184+ < span style = { { color : '#85d996' } } >
185+ "Content-Type: { contentType } "
186+ </ span >
187+ </ span >
188+ </ >
189+ ) }
190+
191+ { body && (
192+ < >
193+ { ' \\' }
194+ < br />
195+ < span >
196+ { ' ' }
197+ -d < span style = { { color : '#85d996' } } > { bodyString } </ span >
198+ </ span >
199+ </ >
200+ ) }
201+ </ code >
152202 ) ;
153203}
154204
@@ -249,6 +299,9 @@ function TryItOut({ item }) {
249299 ] ;
250300
251301 const [ accept , setAccept ] = useState ( acceptArray [ 0 ] ) ;
302+ const [ contentType , setContentType ] = useState (
303+ Object . keys ( item . requestBody ?. content || { } ) [ 0 ]
304+ ) ;
252305 const [ path , setPath ] = useState ( { } ) ;
253306 const [ query , setQuery ] = useState ( { } ) ;
254307 const [ header , setHeader ] = useState ( { } ) ;
@@ -259,6 +312,11 @@ function TryItOut({ item }) {
259312
260313 const [ copyText , setCopyText ] = useState ( 'Copy' ) ;
261314
315+ const [ body , setBody ] = useState ( undefined ) ;
316+
317+ const curlRef = useRef ( null ) ;
318+ // console.log(curlRef);
319+
262320 const requiredParams = item ?. parameters ?. filter ( ( param ) => param . required ) ;
263321
264322 let finishedRequest = true ;
@@ -310,20 +368,16 @@ function TryItOut({ item }) {
310368 setAccept ( e . target . value ) ;
311369 } ;
312370
313- const handleCurlCopy = ( item , path , query , header , cookie , accept ) => ( e ) => {
371+ const handleContentTypeChange = ( e ) => {
372+ setContentType ( e . target . value ) ;
373+ } ;
374+
375+ const handleCurlCopy = ( ) => {
314376 setCopyText ( 'Copied' ) ;
315377 setTimeout ( ( ) => {
316378 setCopyText ( 'Copy' ) ;
317379 } , 2000 ) ;
318- const qs = queryString . stringify ( query ) ;
319-
320- const x = `curl -X ${ item . method . toUpperCase ( ) } "${
321- window . location . origin
322- } ${ item . path . replace ( / { ( [ a - z 0 - 9 - _ ] + ) } / gi, ( _ , p1 ) => {
323- return path [ p1 ] || `:${ p1 } ` ;
324- } ) } ${ qs && '?' } ${ qs } "${ accept && ` -H "accept: ${ accept } "` } `;
325-
326- navigator . clipboard . writeText ( x ) ;
380+ navigator . clipboard . writeText ( curlRef . current . innerText ) ;
327381 } ;
328382
329383 async function buildAndExecute ( item , path , query , header , cookie , accept ) {
@@ -395,8 +449,27 @@ function TryItOut({ item }) {
395449
396450 { /* TODO: Optional params */ }
397451
452+ { /* TODO: Content-Type dropdown */ }
453+ { item . requestBody ?. content &&
454+ Object . keys ( item . requestBody ?. content ) . length > 0 && (
455+ < div className = { styles . formItem } >
456+ < code > Content-Type</ code >
457+ < div >
458+ < select
459+ className = { styles . selectInput }
460+ value = { contentType }
461+ onChange = { handleContentTypeChange }
462+ >
463+ { Object . keys ( item . requestBody ?. content ) . map ( ( type ) => {
464+ return < option value = { type } > { type } </ option > ;
465+ } ) }
466+ </ select >
467+ </ div >
468+ </ div >
469+ ) }
470+
398471 < >
399- { item . requestBody ?. content && (
472+ { item . requestBody ?. content ?. [ 'application/json' ] && (
400473 < div className = { styles . formItem } >
401474 < code > Body</ code >
402475 < div
@@ -408,10 +481,16 @@ function TryItOut({ item }) {
408481 >
409482 { /* monaco */ }
410483 < Editor
411- value = { JSON . stringify ( { example : 'thing' } , null , 2 ) }
484+ value = { JSON . stringify (
485+ sampleFromSchema (
486+ item . requestBody ?. content ?. [ 'application/json' ] ?. schema
487+ ) ,
488+ null ,
489+ 2
490+ ) }
412491 language = "json"
413492 theme = "myCustomTheme"
414- height = "200px"
493+ // height="200px"
415494 options = { {
416495 contentLeft : 0 ,
417496 lineNumbers : 'off' ,
@@ -427,16 +506,47 @@ function TryItOut({ item }) {
427506 lineDecorationsWidth : 0 ,
428507 contextmenu : false ,
429508 } }
430- editorDidMount = { ( _ , editor ) => {
509+ editorDidMount = { ( _valueGetter , editor ) => {
431510 editor . onDidFocusEditorText ( ( ) => {
432511 setEditorFocused ( true ) ;
433512 } ) ;
434513 editor . onDidBlurEditorText ( ( ) => {
435514 setEditorFocused ( false ) ;
436515 } ) ;
516+ editor . onDidChangeModelDecorations ( ( ) => {
517+ updateEditorHeight ( ) ; // typing
518+ requestAnimationFrame ( updateEditorHeight ) ; // folding
519+ setBody ( editor . getValue ( ) ) ;
520+ } ) ;
521+
522+ let prevHeight = 0 ;
523+
524+ const updateEditorHeight = ( ) => {
525+ const editorElement = editor . getDomNode ( ) ;
526+
527+ if ( ! editorElement ) {
528+ return ;
529+ }
530+
531+ const lineHeight = 22 ;
532+ const lineCount =
533+ editor . getModel ( ) ?. getLineCount ( ) || 1 ;
534+ const height =
535+ editor . getTopForLineNumber ( lineCount + 1 ) +
536+ lineHeight ;
537+
538+ const clippedHeight = Math . min ( height , 500 ) ;
539+
540+ if ( prevHeight !== clippedHeight ) {
541+ prevHeight = clippedHeight ;
542+ editorElement . style . height = `${ clippedHeight } px` ;
543+ editor . layout ( ) ;
544+ }
545+ } ;
437546 } }
438547 />
439548
549+ { /* schema: string + binary */ }
440550 { /* <MagicDropzone className={styles.dropzone} onDrop={() => {}}>
441551 <div className={styles.dropzoneContent}>
442552 {item.requestBody.description || 'file upload'}
@@ -466,23 +576,23 @@ function TryItOut({ item }) {
466576 </ div >
467577 ) }
468578 < div className = { styles . floatingButton } >
469- < button
470- onClick = { handleCurlCopy ( item , path , query , header , cookie , accept ) }
471- >
472- { copyText }
473- </ button >
579+ < button onClick = { handleCurlCopy } > { copyText } </ button >
474580 < pre
475581 style = { {
476582 background : '#242526' ,
583+ paddingRight : '60px' ,
477584 } }
478585 >
479586 < Curl
587+ theref = { curlRef }
480588 item = { item }
481589 path = { path }
482590 query = { query }
483591 header = { header }
484592 cookie = { cookie }
485593 accept = { accept }
594+ contentType = { contentType }
595+ body = { body }
486596 />
487597 </ pre >
488598 </ div >
@@ -515,7 +625,15 @@ function TryItOut({ item }) {
515625}
516626
517627function App ( ) {
518- const order = organizeSpec ( spec ) ;
628+ const [ order , setOrder ] = useState ( organizeSpec ( spec ) ) ;
629+
630+ useEffect ( ( ) => {
631+ SwaggerParser . dereference ( spec ) . then ( ( api ) => {
632+ console . log ( api ) ;
633+ // TODO: This ruins our variable names...
634+ setOrder ( organizeSpec ( api ) ) ;
635+ } ) ;
636+ } , [ ] ) ;
519637
520638 const docsSidebars = {
521639 default : order . map ( ( x ) => {
0 commit comments