feat: add try-live poc

This commit is contained in:
cge 2021-12-16 09:58:29 +01:00
parent 6c41e95aa0
commit 1a9d7bea4d
33 changed files with 6662 additions and 1487 deletions

View File

@ -45,6 +45,8 @@ module.exports = {
'unfetch/**',
'raf/polyfill',
'**/fixtures/**', // for tests
'ace-builds/**',
'jsoneditor/**',
],
},
],

View File

@ -0,0 +1,14 @@
JSON Editor Icons
size: outer: 24x24 px
inner: 16x16 px
blue background: RGBA 97b0f8ff
gray background: RGBA 4d4d4dff
grey background: RGBA d3d3d3ff
red foreground: RGBA ff3300ff
green foreground: RGBA 13ae00ff
characters are based on the Arial font

View File

@ -0,0 +1,749 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="240"
height="144"
id="svg4136"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="jsoneditor-icons.svg">
<title
id="title6512">JSON Editor Icons</title>
<metadata
id="metadata4148">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title>JSON Editor Icons</dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs4146" />
<sodipodi:namedview
pagecolor="#ff63ff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1026"
id="namedview4144"
showgrid="true"
inkscape:zoom="4"
inkscape:cx="13.229181"
inkscape:cy="119.82429"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg4136"
showguides="false"
borderlayer="false"
inkscape:showpageshadow="true"
showborder="true">
<inkscape:grid
type="xygrid"
id="grid4640"
empspacing="24" />
</sodipodi:namedview>
<!-- Created with SVG-edit - http://svg-edit.googlecode.com/ -->
<rect
style="fill:#4c4c4c;fill-opacity:1;stroke:none;stroke-width:0"
id="svg_1"
height="16"
width="16"
y="4"
x="4" />
<rect
id="svg_1-7"
height="16"
width="16"
y="3.999995"
x="28.000006"
style="fill:#ec3f29;fill-opacity:0.94117647;stroke:none;stroke-width:0" />
<rect
style="fill:#4c4c4c;fill-opacity:1;stroke:none;stroke-width:0"
x="52.000004"
y="3.999995"
width="16"
height="16"
id="rect4165" />
<rect
id="rect4175"
height="16"
width="16"
y="3.9999852"
x="172.00002"
style="fill:#4c4c4c;fill-opacity:1;stroke:none;stroke-width:0" />
<rect
id="rect4175-3"
height="16"
width="16"
y="3.999995"
x="196"
style="fill:#4c4c4c;fill-opacity:1;stroke:none;stroke-width:0" />
<g
id="g4299"
style="stroke:none">
<rect
x="7.0000048"
y="10.999998"
width="9.9999924"
height="1.9999986"
id="svg_1-1"
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0" />
<rect
x="11.000005"
y="7.0000114"
width="1.9999955"
height="9.9999838"
id="svg_1-1-1"
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0" />
</g>
<g
id="g4299-3"
transform="matrix(0.70710678,-0.70710678,0.70710678,0.70710678,19.029435,12.000001)"
style="stroke:none">
<rect
x="7.0000048"
y="10.999998"
width="9.9999924"
height="1.9999986"
id="svg_1-1-0"
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0" />
<rect
x="11.000005"
y="7.0000114"
width="1.9999955"
height="9.9999838"
id="svg_1-1-1-9"
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0" />
</g>
<rect
id="svg_1-7-5"
height="6.9999905"
width="6.9999909"
y="7.0000048"
x="55.000004"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0" />
<rect
style="fill:#ffffff;fill-opacity:1;stroke:#4c4c4c;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="58"
y="10.00001"
width="6.9999909"
height="6.9999905"
id="rect4354" />
<rect
id="svg_1-7-5-7"
height="6.9999905"
width="6.9999909"
y="10.000005"
x="58.000004"
style="fill:#ffffff;fill-opacity:1;stroke:#3c80df;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.94117647" />
<g
id="g4378">
<rect
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0"
x="198"
y="10.999999"
width="7.9999909"
height="1.9999965"
id="svg_1-7-5-3" />
<rect
id="rect4374"
height="1.9999946"
width="11.999995"
y="7.0000005"
x="198"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0" />
<rect
id="rect4376"
height="1.9999995"
width="3.9999928"
y="14.999996"
x="198"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0" />
</g>
<g
transform="matrix(1,0,0,-1,-23.999995,23.999995)"
id="g4383">
<rect
id="rect4385"
height="1.9999965"
width="7.9999909"
y="10.999999"
x="198"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0" />
<rect
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0"
x="198"
y="7.0000005"
width="11.999995"
height="1.9999946"
id="rect4387" />
<rect
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0"
x="198"
y="14.999996"
width="3.9999928"
height="1.9999995"
id="rect4389" />
</g>
<rect
style="fill:#4c4c4c;fill-opacity:1;stroke:none"
id="rect3754-4"
width="16"
height="16"
x="76"
y="3.9999199" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 85.10447,6.0157384 -0.0156,1.4063 c 3.02669,-0.2402 0.33008,3.6507996 2.48438,4.5780996 -2.18694,1.0938 0.49191,4.9069 -2.45313,4.5781 l -0.0156,1.4219 c 5.70828,0.559 1.03264,-5.1005 4.70313,-5.2656 l 0,-1.4063 c -3.61303,-0.027 1.11893,-5.7069996 -4.70313,-5.3124996 z"
id="path4351"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccc" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 82.78125,5.9984384 0.0156,1.4063 c -3.02668,-0.2402 -0.33007,3.6506996 -2.48437,4.5780996 2.18694,1.0938 -0.49192,4.9069 2.45312,4.5781 l 0.0156,1.4219 c -5.70827,0.559 -1.03263,-5.1004 -4.70312,-5.2656 l 0,-1.4063 c 3.61303,-0.027 -1.11894,-5.7070996 4.70312,-5.3124996 z"
id="path4351-9"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccc" />
<rect
style="fill:#4c4c4c;fill-opacity:1;stroke:none"
id="rect3754-25"
width="16"
height="16"
x="100"
y="3.9999199" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:none"
d="m 103.719,5.6719384 0,12.7187996 3.03125,0 0,-1.5313 -1.34375,0 0,-9.6249996 1.375,0 0,-1.5625 z"
id="path2987"
inkscape:connector-curvature="0" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:none"
d="m 112.2185,5.6721984 0,12.7187996 -3.03125,0 0,-1.5313 1.34375,0 0,-9.6249996 -1.375,0 0,-1.5625 z"
id="path2987-1"
inkscape:connector-curvature="0" />
<rect
style="fill:#4c4c4c;fill-opacity:1;stroke:none"
id="rect3754-73"
width="16"
height="16"
x="124"
y="3.9999199" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:none"
d="m 126.2824,17.602938 1.78957,0 1.14143,-2.8641 5.65364,0 1.14856,2.8641 1.76565,0 -4.78687,-11.1610996 -1.91903,0 z"
id="path3780"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccc" />
<path
style="fill:#4c4c4c;fill-opacity:1;stroke:none"
d="m 129.72704,13.478838 4.60852,0.01 -2.30426,-5.5497996 z"
id="path3782"
inkscape:connector-curvature="0" />
<rect
style="fill:#4c4c4c;fill-opacity:1;stroke:none"
id="rect3754-35"
width="16"
height="16"
x="148"
y="3.9999199" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:none"
d="m 156.47655,5.8917384 0,2.1797 0.46093,2.3983996 1.82813,0 0.39844,-2.3983996 0,-2.1797 z"
id="path5008-2"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:none"
d="m 152.51561,5.8906384 0,2.1797 0.46094,2.3983996 1.82812,0 0.39844,-2.3983996 0,-2.1797 z"
id="path5008-2-8"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc" />
<rect
id="svg_1-7-2"
height="1.9999961"
width="11.999996"
y="64"
x="54"
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0" />
<rect
id="svg_1-7-2-2"
height="2.9999905"
width="2.9999907"
y="52"
x="80.000008"
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0" />
<rect
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
x="85.000008"
y="52"
width="2.9999907"
height="2.9999905"
id="rect4561" />
<rect
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
x="80.000008"
y="58"
width="2.9999907"
height="2.9999905"
id="rect4563" />
<rect
id="rect4565"
height="2.9999905"
width="2.9999907"
y="58"
x="85.000008"
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0" />
<rect
id="rect4567"
height="2.9999905"
width="2.9999907"
y="64"
x="80.000008"
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0" />
<rect
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
x="85.000008"
y="64"
width="2.9999907"
height="2.9999905"
id="rect4569" />
<circle
style="opacity:1;fill:none;fill-opacity:1;stroke:#4c4c4c;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
id="path4571"
cx="110.06081"
cy="57.939209"
r="4.7438836" />
<rect
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
x="116.64566"
y="-31.79752"
width="4.229713"
height="6.4053884"
id="rect4563-2"
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,0,0)" />
<path
style="fill:#4c4c4c;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 125,56 138.77027,56.095 132,64 Z"
id="path4613"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0"
id="path4615"
d="M 149,64 162.77027,63.905 156,56 Z"
style="fill:#4c4c4c;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<rect
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
x="54"
y="53"
width="11.999996"
height="1.9999961"
id="rect4638" />
<rect
id="svg_1-7-2-24"
height="1.9999957"
width="12.99999"
y="-56"
x="53"
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
transform="matrix(0,1,-1,0,0,0)" />
<rect
transform="matrix(0,1,-1,0,0,0)"
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
x="53"
y="-66"
width="12.99999"
height="1.9999957"
id="rect4657" />
<rect
id="rect4659"
height="0.99999291"
width="11.999999"
y="57"
x="54"
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0" />
<rect
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
x="54"
y="88.000122"
width="11.999996"
height="1.9999961"
id="rect4661" />
<rect
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
x="80.000008"
y="76.000122"
width="2.9999907"
height="2.9999905"
id="rect4663" />
<rect
id="rect4665"
height="2.9999905"
width="2.9999907"
y="76.000122"
x="85.000008"
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1" />
<rect
id="rect4667"
height="2.9999905"
width="2.9999907"
y="82.000122"
x="80.000008"
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1" />
<rect
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
x="85.000008"
y="82.000122"
width="2.9999907"
height="2.9999905"
id="rect4669" />
<rect
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
x="80.000008"
y="88.000122"
width="2.9999907"
height="2.9999905"
id="rect4671" />
<rect
id="rect4673"
height="2.9999905"
width="2.9999907"
y="88.000122"
x="85.000008"
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1" />
<circle
r="4.7438836"
cy="81.939331"
cx="110.06081"
id="circle4675"
style="opacity:1;fill:none;fill-opacity:1;stroke:#d3d3d3;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<rect
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,0,0)"
id="rect4677"
height="6.4053884"
width="4.229713"
y="-14.826816"
x="133.6163"
style="fill:#d3d3d3;fill-opacity:1;stroke:#d3d3d3;stroke-width:0;stroke-opacity:1" />
<path
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0"
id="path4679"
d="m 125,80.000005 13.77027,0.09499 L 132,87.999992 Z"
style="fill:#d3d3d3;fill-opacity:1;fill-rule:evenodd;stroke:#d3d3d3;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:#d3d3d3;fill-opacity:1;fill-rule:evenodd;stroke:#d3d3d3;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 149,88.0002 162.77027,87.9052 156,80.0002 Z"
id="path4681"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<rect
id="rect4683"
height="1.9999961"
width="11.999996"
y="77.000122"
x="54"
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1" />
<rect
transform="matrix(0,1,-1,0,0,0)"
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
x="77.000122"
y="-56"
width="12.99999"
height="1.9999957"
id="rect4685" />
<rect
id="rect4687"
height="1.9999957"
width="12.99999"
y="-66"
x="77.000122"
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
transform="matrix(0,1,-1,0,0,0)" />
<rect
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
x="54"
y="81.000122"
width="11.999999"
height="0.99999291"
id="rect4689" />
<rect
id="rect4761-1"
height="1.9999945"
width="15.99999"
y="101"
x="76.000008"
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
<rect
id="rect4761-0"
height="1.9999945"
width="15.99999"
y="105"
x="76.000008"
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
<rect
id="rect4761-7"
height="1.9999945"
width="9"
y="109"
x="76.000008"
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
<rect
id="rect4761-1-1"
height="1.9999945"
width="12"
y="125"
x="76.000008"
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
<rect
id="rect4761-1-1-4"
height="1.9999945"
width="10"
y="137"
x="76.000008"
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
<rect
id="rect4761-1-1-4-4"
height="1.9999945"
width="10"
y="129"
x="82"
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
<rect
id="rect4761-1-1-4-4-3"
height="1.9999945"
width="9"
y="133"
x="82"
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
<path
inkscape:connector-curvature="0"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.8;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.66157866;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 36.398438,100.0254 c -0.423362,-0.013 -0.846847,0.01 -1.265626,0.062 -1.656562,0.2196 -3.244567,0.9739 -4.507812,2.2266 L 29,100.5991 l -2.324219,7.7129 7.826172,-1.9062 -1.804687,-1.9063 c 1.597702,-1.5308 4.048706,-1.8453 5.984375,-0.7207 1.971162,1.1452 2.881954,3.3975 2.308593,5.5508 -0.573361,2.1533 -2.533865,3.6953 -4.830078,3.6953 l 0,3.0742 c 3.550756,0 6.710442,-2.4113 7.650391,-5.9414 0.939949,-3.5301 -0.618463,-7.2736 -3.710938,-9.0703 -1.159678,-0.6738 -2.431087,-1.0231 -3.701171,-1.0625 z"
id="path4138" />
<path
inkscape:connector-curvature="0"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.8;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.66157866;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 59.722656,99.9629 c -1.270084,0.039 -2.541493,0.3887 -3.701172,1.0625 -3.092475,1.7967 -4.650886,5.5402 -3.710937,9.0703 0.939949,3.5301 4.09768,5.9414 7.648437,5.9414 l 0,-3.0742 c -2.296214,0 -4.256717,-1.542 -4.830078,-3.6953 -0.573361,-2.1533 0.337432,-4.4056 2.308594,-5.5508 1.935731,-1.1246 4.38863,-0.8102 5.986326,0.7207 l -1.806638,1.9063 7.828128,1.9062 -2.32422,-7.7129 -1.62696,1.7168 c -1.26338,-1.2531 -2.848917,-2.0088 -4.505855,-2.2285 -0.418778,-0.055 -0.842263,-0.076 -1.265625,-0.062 z"
id="path4138-1" />
<path
inkscape:connector-curvature="0"
style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.96599996;stroke-miterlimit:4;stroke-dasharray:none"
d="m 10.5,100 0,2 -2.4999996,0 L 12,107 l 4,-5 -2.5,0 0,-2 -3,0 z"
id="path3055-0-77" />
<path
style="opacity:0.8;fill:none;stroke:#ffffff;stroke-width:1.96599996;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 4.9850574,108.015 14.0298856,-0.03"
id="path5244-5-0-5"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="opacity:0.8;fill:none;stroke:#ffffff;stroke-width:1.96599996;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 4.9849874,132.015 14.0298866,-0.03"
id="path5244-5-0-5-8"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
inkscape:connector-curvature="0"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.4;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.66157866;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 36.398438,123.9629 c -0.423362,-0.013 -0.846847,0.01 -1.265626,0.062 -1.656562,0.2196 -3.244567,0.9739 -4.507812,2.2266 L 29,124.5366 l -2.324219,7.7129 7.826172,-1.9062 -1.804687,-1.9063 c 1.597702,-1.5308 4.048706,-1.8453 5.984375,-0.7207 1.971162,1.1453 2.881954,3.3975 2.308593,5.5508 -0.573361,2.1533 -2.533864,3.6953 -4.830078,3.6953 l 0,3.0742 c 3.550757,0 6.710442,-2.4093 7.650391,-5.9394 0.939949,-3.5301 -0.618463,-7.2756 -3.710938,-9.0723 -1.159678,-0.6737 -2.431087,-1.0231 -3.701171,-1.0625 z"
id="path4138-12" />
<path
inkscape:connector-curvature="0"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.4;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.66157866;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 59.722656,123.9629 c -1.270084,0.039 -2.541493,0.3888 -3.701172,1.0625 -3.092475,1.7967 -4.650886,5.5422 -3.710937,9.0723 0.939949,3.5301 4.09768,5.9394 7.648437,5.9394 l 0,-3.0742 c -2.296214,0 -4.256717,-1.542 -4.830078,-3.6953 -0.573361,-2.1533 0.337432,-4.4055 2.308594,-5.5508 1.935731,-1.1246 4.38863,-0.8102 5.986326,0.7207 l -1.806638,1.9063 7.828128,1.9062 -2.32422,-7.7129 -1.62696,1.7168 c -1.26338,-1.2531 -2.848917,-2.0088 -4.505855,-2.2285 -0.418778,-0.055 -0.842263,-0.076 -1.265625,-0.062 z"
id="path4138-1-3" />
<path
id="path6191"
d="m 10.5,116 0,-2 -2.4999996,0 L 12,109 l 4,5 -2.5,0 0,2 -3,0 z"
style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.96599996;stroke-miterlimit:4;stroke-dasharray:none"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.96599996;stroke-miterlimit:4;stroke-dasharray:none"
d="m 10.5,129 0,-2 -2.4999996,0 L 12,122 l 4,5 -2.5,0 0,2 -3,0 z"
id="path6193" />
<path
id="path6195"
d="m 10.5,135 0,2 -2.4999996,0 L 12,142 l 4,-5 -2.5,0 0,-2 -3,0 z"
style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.96599996;stroke-miterlimit:4;stroke-dasharray:none"
inkscape:connector-curvature="0" />
<path
sodipodi:type="star"
style="fill:#4d4d4d;fill-opacity:0.90196078;stroke:#d3d3d3;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
id="path4500"
sodipodi:sides="3"
sodipodi:cx="11.55581"
sodipodi:cy="60.073242"
sodipodi:r1="5.1116104"
sodipodi:r2="2.5558052"
sodipodi:arg1="0"
sodipodi:arg2="1.0471976"
inkscape:flatsided="false"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 16.66742,60.073242 -3.833708,2.213392 -3.8337072,2.213393 0,-4.426785 0,-4.426784 3.8337082,2.213392 z"
inkscape:transform-center-x="-1.2779026" />
<path
inkscape:transform-center-x="1.277902"
d="m -31.500004,60.073242 -3.833708,2.213392 -3.833707,2.213393 0,-4.426785 0,-4.426784 3.833707,2.213392 z"
inkscape:randomized="0"
inkscape:rounded="0"
inkscape:flatsided="false"
sodipodi:arg2="1.0471976"
sodipodi:arg1="0"
sodipodi:r2="2.5558052"
sodipodi:r1="5.1116104"
sodipodi:cy="60.073242"
sodipodi:cx="-36.611614"
sodipodi:sides="3"
id="path4502"
style="fill:#4d4d4d;fill-opacity:0.90196078;stroke:#d3d3d3;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
sodipodi:type="star"
transform="scale(-1,1)" />
<path
d="m 16.66742,60.073212 -3.833708,2.213392 -3.8337072,2.213392 0,-4.426784 0,-4.426785 3.8337082,2.213392 z"
inkscape:randomized="0"
inkscape:rounded="0"
inkscape:flatsided="false"
sodipodi:arg2="1.0471976"
sodipodi:arg1="0"
sodipodi:r2="2.5558052"
sodipodi:r1="5.1116104"
sodipodi:cy="60.073212"
sodipodi:cx="11.55581"
sodipodi:sides="3"
id="path4504"
style="fill:#4d4d4d;fill-opacity:0.90196078;stroke:#d3d3d3;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
sodipodi:type="star"
transform="matrix(0,1,-1,0,72.0074,71.7877)"
inkscape:transform-center-y="1.2779029" />
<path
inkscape:transform-center-y="-1.2779026"
transform="matrix(0,-1,-1,0,96,96)"
sodipodi:type="star"
style="fill:#4d4d4d;fill-opacity:0.90196078;stroke:#d3d3d3;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
id="path4506"
sodipodi:sides="3"
sodipodi:cx="11.55581"
sodipodi:cy="60.073212"
sodipodi:r1="5.1116104"
sodipodi:r2="2.5558052"
sodipodi:arg1="0"
sodipodi:arg2="1.0471976"
inkscape:flatsided="false"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 16.66742,60.073212 -3.833708,2.213392 -3.8337072,2.213392 0,-4.426784 0,-4.426785 3.8337082,2.213392 z" />
<path
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0"
id="path4615-5"
d="m 171.82574,65.174193 16.34854,0 -8.17427,-13.348454 z"
style="fill:#fbb917;fill-opacity:1;fill-rule:evenodd;stroke:#fbb917;stroke-width:1.65161395;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 179,55 0,6 2,0 0,-6"
id="path4300"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 179,62 0,2 2,0 0,-2"
id="path4300-6"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
style="fill:#ffffff;fill-opacity:0.8;fill-rule:evenodd;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:0.8"
d="M 99.994369,113.0221 102,114.98353 l 7,-6.9558 3,0.97227 2,-1 1,-2 0,-3 -3,3 -3,-3 3,-3 -3,0 -2,1 -1,2 0.99437,3.0221 z"
id="path4268"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccccccccc" />
<rect
id="rect4175-3-5"
height="16"
width="16"
y="4"
x="220"
style="fill:#4c4c4c;fill-opacity:1;stroke:none;stroke-width:0" />
<path
style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 234,6 0,2 -5,5 0,5 -2,0 0,-5 -5,-5 0,-2"
id="path3546"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccc" />
<g
transform="matrix(1.3333328,0,0,-1.5999992,-139.9999,127.19999)"
id="g4383-6">
<rect
id="rect4385-2"
height="1.2499905"
width="5.9999924"
y="12.625005"
x="198.00002"
style="fill:#ffffff;fill-opacity:0.8;stroke:#000000;stroke-width:0" />
<rect
style="fill:#ffffff;fill-opacity:0.8;stroke:#000000;stroke-width:0"
x="198.00002"
y="15.125007"
width="7.4999928"
height="1.2499949"
id="rect4387-9" />
<rect
style="fill:#ffffff;fill-opacity:0.8;stroke:#000000;stroke-width:0"
x="198.00002"
y="7.6250024"
width="2.9999909"
height="1.2499905"
id="rect4389-1-0" />
<rect
style="fill:#ffffff;fill-opacity:0.8;stroke:#000000;stroke-width:0"
x="198.00002"
y="10.125004"
width="4.4999919"
height="1.2499905"
id="rect4389-1-9" />
<path
style="fill:#ffffff;fill-opacity:0.8;fill-rule:evenodd;stroke:none;stroke-width:0.68465352px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 207.00001,16.375004 0,-5.625005 -2.25,0 3,-3.1250014 3,3.1250014 -2.25,0 0,5.625005 -1.5,0"
id="path4402"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccc" />
</g>
<path
style="fill:#ffffff;fill-opacity:0.8;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 164,100 0,3 -6,6 0,7 -4,0 0,-7 -6,-6 0,-3"
id="path3546-2-2"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccc" />
<rect
style="fill:#4c4c4c;fill-opacity:1;stroke:none;stroke-width:0"
id="svg_1-3"
height="16"
width="16"
y="28"
x="4" />
<path
sodipodi:nodetypes="ccccccccc"
inkscape:connector-curvature="0"
id="path4402-5-7"
d="m 15,41 0,-7 -4,0 0,3 -5,-4 5,-4 0,3 6,0 0,9"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.68465352px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
</svg>

After

Width:  |  Height:  |  Size: 31 KiB

2025
demo/css/jsoneditor.css Normal file

File diff suppressed because it is too large Load Diff

71
demo/css/styles.css Normal file
View File

@ -0,0 +1,71 @@
.jsoneditor {
border-color: transparent;
border-bottom: thin solid #262626;
}
.ace_editor {
border-top: thin solid #262626;
}
.jsoneditor-poweredBy {
display: none;
}
.jsoneditor-menu {
background-color: transparent;
border-color: transparent;
}
.je-wrapper {
margin-top: -4px;
}
.je-ft-resize {
border-color: rgba(38, 50, 56, 0.8);
}
/* comment everything below for defaults */
.jsoneditor-statusbar,
.ace-twilight .ace_gutter {
border: none;
}
.jsoneditor-statusbar,
.ace-twilight .ace_gutter {
background-color: #171c20;
border-color: #171c20;
color: grey;
}
.ace-twilight {
background-color: #111;
color: #f8f8f8;
font-family: Courier, monospace;
font-size: 13px;
line-height: 20px;
}
.ace-twilight .ace_variable {
color: #ffffff;
}
.ace-twilight .ace_string {
color: #a0fbaa;
}
.ace-twilight .ace_constant,
.ace-twilight .ace_constant.ace_character,
.ace-twilight .ace_constant.ace_character.ace_escape,
.ace-twilight .ace_constant.ace_other,
.ace-twilight .ace_heading,
.ace-twilight .ace_markup.ace_heading,
.ace-twilight .ace_support.ace_constant {
color: firebrick;
}
.ace-twilight .boolean {
color: firebrick;
}
.ace-twilight .ace_numeric {
color: firebrick;
}

77
demo/css/styles.scss Normal file
View File

@ -0,0 +1,77 @@
$editor-top-bottom-line-color: #262626;
$editor-gutter-status-color: #171c20;
$editor-gutter-status-text-color: grey;
$editor-background-color: #111; // #141414
$editor-text-color: #f8f8f8;
.jsoneditor {
border-color: transparent;
border-bottom: thin solid $editor-top-bottom-line-color;
}
.ace_editor {
border-top: thin solid $editor-top-bottom-line-color;
}
.jsoneditor-poweredBy {
display: none;
}
.jsoneditor-menu {
background-color: transparent;
border-color: transparent;
}
.je-wrapper {
margin-top: -4px;
}
.je-ft-resize {
border-color: rgba(38, 50, 56, 0.8);
}
// comment everything below for defaults
.jsoneditor-statusbar,
.ace-twilight .ace_gutter {
border: none;
}
.jsoneditor-statusbar,
.ace-twilight .ace_gutter {
background-color: $editor-gutter-status-color;
border-color: $editor-gutter-status-color;
color: $editor-gutter-status-text-color;
}
.ace-twilight {
background-color: $editor-background-color;
color: $editor-text-color;
font-family: Courier, monospace;
font-size: 13px;
line-height: 20px;
}
.ace-twilight .ace_variable {
color: #ffffff;
}
.ace-twilight .ace_string {
color: #a0fbaa;
}
.ace-twilight .ace_constant,
.ace-twilight .ace_constant.ace_character,
.ace-twilight .ace_constant.ace_character.ace_escape,
.ace-twilight .ace_constant.ace_other,
.ace-twilight .ace_heading,
.ace-twilight .ace_markup.ace_heading,
.ace-twilight .ace_support.ace_constant {
color: firebrick;
}
.ace-twilight .boolean {
color: firebrick;
}
.ace-twilight .ace_numeric {
color: firebrick;
}

View File

@ -1,16 +1,24 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>ReDoc Interactive Demo</title>
<meta name="description" content="ReDoc Interactive Demo. OpenAPI/Swagger-generated API Reference Documentation" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta
name="description"
content="ReDoc Interactive Demo. OpenAPI/Swagger-generated API Reference Documentation"
/>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta property="og:title" content="ReDoc Interactive Demo">
<meta property="og:description" content="ReDoc Interactive Demo. OpenAPI/Swagger-generated API Reference Documentation">
<meta property="og:image" content="https://user-images.githubusercontent.com/3975738/37729752-8a9ea38a-2d46-11e8-8438-42ed26bf1751.png">
<meta name="twitter:card" content="summary_large_image">
<meta property="og:title" content="ReDoc Interactive Demo" />
<meta
property="og:description"
content="ReDoc Interactive Demo. OpenAPI/Swagger-generated API Reference Documentation"
/>
<meta
property="og:image"
content="https://user-images.githubusercontent.com/3975738/37729752-8a9ea38a-2d46-11e8-8438-42ed26bf1751.png"
/>
<meta name="twitter:card" content="summary_large_image" />
<style>
body {
@ -22,13 +30,16 @@
display: block;
}
</style>
<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
<link
href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700"
rel="stylesheet"
/>
</head>
<body>
<div id="container"></div>
<script>
<!-- <script>
(function (i, s, o, g, r, a, m) {
i['GoogleAnalyticsObject'] = r; i[r] = i[r] || function () {
(i[r].q = i[r].q || []).push(arguments)
@ -40,7 +51,6 @@
ga('create', 'UA-81703547-1', 'auto');
ga('send', 'pageview');
}
</script>
</script> -->
</body>
</html>

View File

@ -50,6 +50,7 @@ info:
x-logo:
url: 'https://redocly.github.io/redoc/petstore-logo.png'
altText: Petstore logo
disabled: true
license:
name: Apache 2.0
url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
@ -88,7 +89,7 @@ paths:
parameters:
- name: Accept-Language
in: header
description: "The language you prefer for messages. Supported values are en-AU, en-CA, en-GB, en-US"
description: 'The language you prefer for messages. Supported values are en-AU, en-CA, en-GB, en-US'
example: en-US
required: false
schema:
@ -254,7 +255,7 @@ paths:
required: false
schema:
type: string
example: "Bearer <TOKEN>"
example: 'Bearer <TOKEN>'
- name: petId
in: path
description: Pet id to delete
@ -429,7 +430,7 @@ paths:
application/json:
example:
status: 400
message: "Invalid Order"
message: 'Invalid Order'
requestBody:
content:
application/json:
@ -1027,8 +1028,8 @@ components:
properties:
id:
externalDocs:
description: "Find more info here"
url: "https://example.com"
description: 'Find more info here'
url: 'https://example.com'
description: Pet ID
allOf:
- $ref: '#/components/schemas/Id'
@ -1201,7 +1202,7 @@ x-webhooks:
content:
application/json:
schema:
$ref: "#/components/schemas/Pet"
$ref: '#/components/schemas/Pet'
responses:
"200":
'200':
description: Return a 200 status to indicate that the data was received successfully

View File

@ -1,9 +1,8 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>ReDoc</title>
<style>
body {
@ -15,11 +14,15 @@
display: block;
}
</style>
<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
<link
href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700"
rel="stylesheet"
/>
<link href="./css/jsoneditor.css" rel="stylesheet" />
<link href="./css/styles.css" rel="stylesheet" />
</head>
<body>
<redoc id="example"></redoc>
</body>
</html>

View File

@ -69,7 +69,11 @@ export default (env: { playground?: boolean; bench?: boolean } = {}, { mode }) =
module: {
rules: [
{ test: [/\.eot$/, /\.gif$/, /\.woff$/, /\.svg$/, /\.ttf$/], use: 'null-loader' },
{ test: [/\.eot$/, /\.gif$/, /\.woff$/, /\.ttf$/], use: 'null-loader' },
{
test: /\.svg$/,
use: ['@svgr/webpack', 'url-loader'],
},
{
test: /\.tsx?$/,
use: [getBabelLoader({ useBuiltIns: true, hot: true })],

3821
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -76,6 +76,7 @@
"@cypress/webpack-preprocessor": "^5.9.0",
"@hot-loader/react-dom": "^17.0.1",
"@size-limit/preset-app": "^7.0.4",
"@svgr/webpack": "^6.1.2",
"@types/chai": "^4.2.18",
"@types/dompurify": "^2.2.2",
"@types/enzyme": "^3.10.5",
@ -138,6 +139,7 @@
"ts-node": "^10.0.0",
"typescript": "~4.1.0",
"unfetch": "^4.2.0",
"url-loader": "^4.1.1",
"url-polyfill": "^1.1.12",
"webpack": "^5.38.1",
"webpack-cli": "^4.7.2",
@ -156,11 +158,14 @@
"@babel/runtime": "^7.14.0",
"@redocly/openapi-core": "^1.0.0-beta.54",
"@redocly/react-dropdown-aria": "^2.0.11",
"ace-builds": "^1.4.13",
"axios": "^0.24.0",
"classnames": "^2.3.1",
"decko": "^1.2.0",
"dompurify": "^2.2.8",
"eventemitter3": "^4.0.7",
"json-pointer": "^0.6.1",
"jsoneditor": "^9.5.7",
"lunr": "^2.3.9",
"mark.js": "^8.11.1",
"marked": "^3.0.4",

View File

@ -135,6 +135,30 @@ export const SimpleDropdown = styled(StyledDropdown)`
}
`;
export const SimpleDropdown2 = styled(SimpleDropdown)`
&& {
border: 1px solid #999;
width: 100%;
margin: 0;
padding: 5px;
.dropdown-selector {
padding: 0 5px;
}
.dropdown-option {
padding: 5px 10px;
}
.dropdown-selector-content {
padding: 0;
}
&:hover,
&:focus-within {
border: 1px solid #32329f;
}
}
`;
export const MimeLabel = styled.span`
margin-left: 10px;
text-transform: none;

View File

@ -40,6 +40,14 @@ export const RightPanelHeader = styled.h3`
${extensionsHook('RightPanelHeader')};
`;
export const RightPanelHeaderDiv = styled.div`
color: ${({ theme }) => theme.rightPanel.textColor};
font-family: Roboto, sans-serif;
padding: 4px 0;
${extensionsHook('RightPanelHeaderDiv')};
`;
export const UnderlinedHeader = styled.h5`
border-bottom: 1px solid rgba(38, 50, 56, 0.3);
margin: 1em 0 1em 0;

View File

@ -1,5 +1,6 @@
import { observer } from 'mobx-react';
import * as React from 'react';
import { SimpleDropdown2 as Dropdown } from '../../common-elements/dropdown';
import { AppStore } from '../../services/AppStore';
@ -22,6 +23,10 @@ export interface ApiInfoProps {
@observer
export class ApiInfo extends React.Component<ApiInfoProps> {
handleServerSelect = option => {
this.props.store.spec.setSelectedServerIndex(option.idx);
};
handleDownloadClick = e => {
if (!e.target.href) {
e.target.href = this.props.store.spec.info.downloadLink;
@ -30,9 +35,8 @@ export class ApiInfo extends React.Component<ApiInfoProps> {
render() {
const { store } = this.props;
const { info, externalDocs } = store.spec;
const { info, externalDocs, selectedServerIndex, servers } = store.spec;
const hideDownloadButton = store.options.hideDownloadButton;
const downloadFilename = info.downloadFileName;
const downloadLink = info.downloadLink;
@ -76,6 +80,11 @@ export class ApiInfo extends React.Component<ApiInfoProps> {
const version = (info.version && <span>({info.version})</span>) || null;
const serverItems = (servers || []).map((item, index) => ({
value: `${item.url} (${item.description})`,
idx: index,
}));
return (
<Section>
<Row>
@ -109,6 +118,12 @@ export class ApiInfo extends React.Component<ApiInfoProps> {
<Markdown source={store.spec.info.summary} data-role="redoc-summary" />
<Markdown source={store.spec.info.description} data-role="redoc-description" />
{externalDocs && <ExternalDocumentation externalDocs={externalDocs} />}
<Markdown source="Servers" data-role="list-servers" />
<Dropdown
value={serverItems[selectedServerIndex].value}
options={serverItems}
onChange={this.handleServerSelect}
/>
</MiddlePanel>
</Row>
</Section>

View File

@ -12,7 +12,7 @@ export class ApiLogo extends React.Component<{ info: OpenAPIInfo }> {
return null;
}
const logoHref = logoInfo.href || (info.contact && info.contact.url);
const logoHref = !logoInfo.disabled && (logoInfo.href || (info.contact && info.contact.url));
// Use the english word logo if no alt text is provided
const altText = logoInfo.altText ? logoInfo.altText : 'logo';

View File

@ -1,19 +1,11 @@
import * as React from 'react';
import { ShelfIcon } from '../../common-elements';
import { OperationModel } from '../../services';
import { Markdown } from '../Markdown/Markdown';
import { OptionsContext } from '../OptionsProvider';
import { SelectOnClick } from '../SelectOnClick/SelectOnClick';
import { expandDefaultServerVariables, getBasePath } from '../../utils';
import {
EndpointInfo,
HttpVerb,
OperationEndpointWrap,
ServerItem,
ServerRelativeURL,
ServersOverlay,
ServerUrl,
} from './styled.elements';
export interface EndpointProps {
@ -36,60 +28,19 @@ export class Endpoint extends React.Component<EndpointProps, EndpointState> {
};
}
toggle = () => {
this.setState({ expanded: !this.state.expanded });
};
render() {
const { operation, inverted, hideHostname } = this.props;
const { operation, inverted } = this.props;
const { expanded } = this.state;
// TODO: highlight server variables, e.g. https://{user}.test.com
return (
<OptionsContext.Consumer>
{options => (
<OperationEndpointWrap>
<EndpointInfo onClick={this.toggle} expanded={expanded} inverted={inverted}>
<EndpointInfo expanded={expanded} inverted={inverted}>
<HttpVerb type={operation.httpVerb} compact={this.props.compact}>
{operation.httpVerb}
</HttpVerb>
<ServerRelativeURL>{operation.path}</ServerRelativeURL>
<ShelfIcon
float={'right'}
color={inverted ? 'black' : 'white'}
size={'20px'}
direction={expanded ? 'up' : 'down'}
style={{ marginRight: '-25px' }}
/>
</EndpointInfo>
<ServersOverlay expanded={expanded} aria-hidden={!expanded}>
{operation.servers.map(server => {
const normalizedUrl = options.expandDefaultServerVariables
? expandDefaultServerVariables(server.url, server.variables)
: server.url;
const basePath = getBasePath(normalizedUrl);
return (
<ServerItem key={normalizedUrl}>
<Markdown source={server.description || ''} compact={true} />
<SelectOnClick>
<ServerUrl>
<span>
{hideHostname || options.hideHostname
? basePath === '/'
? ''
: basePath
: normalizedUrl}
</span>
{operation.path}
</ServerUrl>
</SelectOnClick>
</ServerItem>
);
})}
</ServersOverlay>
</OperationEndpointWrap>
)}
</OptionsContext.Consumer>
);
}
}

View File

@ -1,7 +1,6 @@
import styled from '../../styled-components';
export const OperationEndpointWrap = styled.div`
cursor: pointer;
position: relative;
margin-bottom: 5px;
`;
@ -9,23 +8,20 @@ export const OperationEndpointWrap = styled.div`
export const ServerRelativeURL = styled.span`
font-family: ${props => props.theme.typography.code.fontFamily};
margin-left: 10px;
flex: 1;
overflow-x: hidden;
text-overflow: ellipsis;
word-break: break-all;
overflow-wrap: anywhere;
`;
export const EndpointInfo = styled.button<{ expanded?: boolean; inverted?: boolean }>`
export const EndpointInfo = styled.div<{ expanded?: boolean; inverted?: boolean }>`
outline: 0;
color: inherit;
width: 100%;
text-align: left;
cursor: pointer;
padding: 10px 30px 10px ${props => (props.inverted ? '10px' : '20px')};
padding: 10px 20px;
border-radius: ${props => (props.inverted ? '0' : '4px 4px 0 0')};
background-color: ${props =>
props.inverted ? 'transparent' : props.theme.codeBlock.backgroundColor};
display: flex;
white-space: nowrap;
align-items: center;
border: ${props => (props.inverted ? '0' : '1px solid transparent')};
border-bottom: ${props => (props.inverted ? '1px solid #ccc' : '0')};
@ -37,9 +33,6 @@ export const EndpointInfo = styled.button<{ expanded?: boolean; inverted?: boole
.${ServerRelativeURL} {
color: ${props => (props.inverted ? props.theme.colors.text.primary : '#ffffff')};
}
&:focus {
box-shadow: inset 0 2px 2px rgba(0, 0, 0, 0.45), 0 2px 0 rgba(128, 128, 128, 0.25);
}
`;
export const HttpVerb = styled.span.attrs((props: { type: string; compact?: boolean }) => ({

View File

@ -0,0 +1,192 @@
import * as React from 'react';
import { cloneDeep, isEqual } from 'lodash';
import JSONEditorRaw from 'jsoneditor';
import 'jsoneditor/dist/jsoneditor.css';
import 'ace-builds/src-noconflict/theme-monokai.js';
import 'ace-builds/src-noconflict/theme-twilight.js';
import 'ace-builds/src-noconflict/theme-ambiance.js';
import { JeFt, JeFtResize } from './styled.elements';
import { EmptyObject } from '../../types';
export enum Mode {
tree = 'tree',
form = 'form',
view = 'view',
code = 'code',
text = 'text',
}
export enum Theme {
monokai = 'ace/theme/monokai',
twilight = 'ace/theme/twilight',
ambiance = 'ace/theme/ambiance',
}
export type TMode = keyof typeof Mode;
interface IJSONEditorRaw extends Object {
set: (json?: EmptyObject) => void;
setText: (text?: string) => void;
update: (json?: EmptyObject) => void;
updateText: (text?: string) => void;
setMode: (mode?: Mode) => void;
setSchema: (schema?: EmptyObject, schemaRefs?: EmptyObject) => void;
destroy: () => void;
resize: () => void;
setTheme: (theme: string) => void;
ace: any;
}
interface IJsonEditor {
json?: EmptyObject;
text?: string;
search?: boolean;
schema?: EmptyObject;
schemaRefs?: EmptyObject;
mode?: Mode;
modes?: Mode[];
indentation?: number;
modalAnchor?: HTMLElement;
enableSort?: boolean;
enableTransform?: boolean;
onChangeText?: (text: string) => void;
onChangeJSON?: (json: EmptyObject) => void;
onModeChange?: (mode: Mode) => void;
theme?: string;
}
interface JsonEditorState {
contentMinH: number;
contentH: number;
}
class JsonEditor extends React.Component<IJsonEditor, JsonEditorState> {
jsoneditor?: IJSONEditorRaw | null = null;
schema?: EmptyObject;
schemaRefs?: EmptyObject;
content: HTMLDivElement | null = null;
container: HTMLDivElement | null = null;
startResizeY?: number;
startHeight?: number;
state = {
contentMinH: 260,
contentH: 260,
};
componentDidMount() {
const { json, text, ...opts } = this.props;
this.jsoneditor = new JSONEditorRaw(this.container, opts);
if (!this.jsoneditor) return;
if (json) {
this.jsoneditor.set(json);
}
if (text) {
this.jsoneditor.setText(text);
}
this.schema = cloneDeep(opts.schema);
this.schemaRefs = cloneDeep(opts.schemaRefs);
}
componentDidUpdate() {
if (!this.jsoneditor) return;
const { json, text, mode, schema, schemaRefs } = this.props;
if (json) {
this.jsoneditor.update(json);
}
if (text) {
this.jsoneditor.updateText(text);
}
if (mode) {
this.jsoneditor.setMode(mode);
}
// store a clone of the schema to keep track of when it actually changes
const schemaChanged = !isEqual(schema, this.schema);
const schemaRefsChanged = !isEqual(schemaRefs, this.schemaRefs);
if (schemaChanged || schemaRefsChanged) {
this.schema = cloneDeep(schema);
this.schemaRefs = cloneDeep(schemaRefs);
this.jsoneditor.setSchema(schema, schemaRefs);
}
}
componentWillUnmount() {
if (this.jsoneditor) {
this.jsoneditor.destroy();
}
}
focusView = () => {
if (this.container) {
this.container.focus();
}
};
resizeMove = event => {
event.preventDefault();
const deltaY = this.startResizeY! - event.clientY;
this.setState(state => ({
...state,
contentH: Math.max(state.contentMinH, this.startHeight! - deltaY),
}));
};
resizeUp = event => {
event.preventDefault();
document.removeEventListener('mousemove', this.resizeMove);
document.removeEventListener('mouseup', this.resizeUp);
};
onResizeDown = event => {
event.preventDefault();
this.startResizeY = event.clientY;
this.startHeight = this.content ? this.content.clientHeight : this.state.contentMinH;
document.addEventListener('mousemove', this.resizeMove);
document.addEventListener('mouseup', this.resizeUp);
};
render() {
if (this.jsoneditor) {
// delay said not to be necessary.. we have to call this for proper editor resizing
// in code or text modes
this.jsoneditor.resize();
}
return (
<div>
<div
ref={node => {
this.content = node;
}}
className="je-content"
style={{
minHeight: this.state.contentMinH,
height: this.state.contentH,
}}
onClick={this.focusView}
>
<div
className="jsoneditor-react-container"
ref={node => (this.container = node)}
style={{
width: '100%',
minHeight: this.state.contentMinH,
height: this.state.contentH,
}}
/>
</div>
<JeFt className="je-ft" onMouseDown={this.onResizeDown}>
<JeFtResize className="je-ft-resize">
<span></span>
</JeFtResize>
</JeFt>
</div>
);
}
}
export default JsonEditor;

View File

@ -0,0 +1,108 @@
import * as React from 'react';
import JsonEditor, { Mode, Theme } from './JsonEditor';
import { EmptyObject } from '../../types';
// const schema = {
// title: 'Example Schema',
// type: 'object',
// properties: {
// array: {
// type: 'array',
// items: {
// type: 'number'
// }
// },
// boolean: {
// type: 'boolean',
// },
// number: {
// type: 'number'
// }
// },
// required: ['array', 'boolean']
// };
// const json = {
// array: [1, 2, 3],
// boolean: true,
// string: 'four'
// }
const modes = [];
interface JsonEditorWrapperProps {
schema?: EmptyObject;
text?: string;
json?: EmptyObject;
onChange?: (value: any) => void;
theme?: string;
}
interface JsonEditorWrapperState {
schema?: EmptyObject;
text?: string;
json?: EmptyObject;
mode: Mode;
}
// TODO
// - messy json / text interplay
// - schema validation
class JsonEditorWrapper extends React.Component<JsonEditorWrapperProps, JsonEditorWrapperState> {
constructor(props: JsonEditorWrapperProps) {
super(props);
const { schema, text } = this.props;
const defaultSchema = {};
this.state = {
schema: schema || defaultSchema,
text: text || '',
json: {}, //text || '',
mode: Mode.code,
};
}
onChangeText = (text: string) => {
this.setState({ text });
if (this.props.onChange) {
this.props.onChange(text);
}
};
onChangeJSON = (text: string) => {
this.setState({ text });
if (this.props.onChange) {
this.props.onChange(text);
}
};
onModeChange = (mode: Mode) => {
this.setState({ mode });
};
render() {
const { theme } = this.props;
const { schema, text, mode } = this.state;
return (
<div className="je-wrapper">
<JsonEditor
search={false}
schema={schema}
text={text}
mode={mode}
modes={modes}
theme={theme || Theme.twilight}
indentation={4}
enableSort={false}
enableTransform={false}
onChangeText={this.onChangeText}
// onChangeJSON={this.onChangeJSON}
onModeChange={this.onModeChange}
/>
</div>
);
}
}
export default JsonEditorWrapper;

View File

@ -0,0 +1,23 @@
import styled from '../../styled-components';
export const JeFt = styled.div`
background-color: rgba(38, 50, 56, 0.4);
border-color: rgba(38, 50, 56, 0.5);
color: #999;
margin-top: -2px;
width: 100%;
height: 28px;
cursor: pointer !important;
`;
export const JeFtResize = styled.div`
position: relative;
line-height: normal;
user-select: none;
> span {
position: absolute;
right: 3px;
top: 7px;
}
`;

View File

@ -17,6 +17,7 @@ import { RequestSamples } from '../RequestSamples/RequestSamples';
import { ResponsesList } from '../Responses/ResponsesList';
import { ResponseSamples } from '../ResponseSamples/ResponseSamples';
import { SecurityRequirements } from '../SecurityRequirement/SecurityRequirement';
import { StoreContext } from '..';
const Description = styled.div`
margin-bottom: ${({ theme }) => theme.spacing.unit * 6}px;
@ -37,6 +38,8 @@ export class Operation extends React.Component<OperationProps> {
return (
<OptionsContext.Consumer>
{options => (
<StoreContext.Consumer>
{store => (
<Row>
<MiddlePanel>
<H2>
@ -61,12 +64,14 @@ export class Operation extends React.Component<OperationProps> {
</MiddlePanel>
<DarkRightPanel>
{!options.pathInMiddlePanel && !isWebhook && <Endpoint operation={operation} />}
<RequestSamples operation={operation} />
<RequestSamples operation={operation} store={store} />
<ResponseSamples operation={operation} />
<CallbackSamples callbacks={operation.callbacks} />
</DarkRightPanel>
</Row>
)}
</StoreContext.Consumer>
)}
</OptionsContext.Consumer>
);
}

View File

@ -8,24 +8,42 @@ import { useExternalExample } from './exernalExampleHook';
export interface ExampleProps {
example: ExampleModel;
mimeType: string;
editable?: boolean;
value?: any;
onChange?: (value: any) => void;
}
export function Example({ example, mimeType }: ExampleProps) {
export function Example({ example, mimeType, editable = false, value, onChange }: ExampleProps) {
if (example.value === undefined && example.externalValueUrl) {
return <ExternalExample example={example} mimeType={mimeType} />;
return (
<ExternalExample
example={example}
mimeType={mimeType}
editable={editable}
value={value}
onChange={onChange}
/>
);
} else {
return <ExampleValue value={example.value} mimeType={mimeType} />;
return (
<ExampleValue
value={value || example.value}
mimeType={mimeType}
editable={editable}
onChange={onChange}
/>
);
}
}
export function ExternalExample({ example, mimeType }: ExampleProps) {
const value = useExternalExample(example, mimeType);
export function ExternalExample({ example, mimeType, editable, value, onChange }: ExampleProps) {
const value_ = useExternalExample(example, mimeType);
if (value === undefined) {
if (value_ === undefined) {
return <span>Loading...</span>;
}
if (value instanceof Error) {
if (value_ instanceof Error) {
return (
<StyledPre>
Error loading external example: <br />
@ -41,5 +59,12 @@ export function ExternalExample({ example, mimeType }: ExampleProps) {
);
}
return <ExampleValue value={value} mimeType={mimeType} />;
return (
<ExampleValue
value={value || value_}
mimeType={mimeType}
editable={editable}
onChange={onChange}
/>
);
}

View File

@ -3,15 +3,32 @@ import * as React from 'react';
import { isJsonLike, langFromMime } from '../../utils/openapi';
import { JsonViewer } from '../JsonViewer/JsonViewer';
import { SourceCodeWithCopy } from '../SourceCode/SourceCode';
import { OptionsConsumer } from '..';
import { JsonEditorWrapper } from '../';
export interface ExampleValueProps {
value: any;
mimeType: string;
editable?: boolean;
onChange?: (value: any) => void;
}
export function ExampleValue({ value, mimeType }: ExampleValueProps) {
export function ExampleValue({ value, mimeType, editable, onChange }: ExampleValueProps) {
if (isJsonLike(mimeType)) {
return <JsonViewer data={value} />;
return editable ? (
<OptionsConsumer>
{options => (
<JsonEditorWrapper
schema={{}}
text={JSON.stringify(value, null, 2)}
onChange={onChange}
theme={options.tryLiveEditorTheme}
/>
)}
</OptionsConsumer>
) : (
<JsonViewer data={value} />
);
} else {
if (typeof value === 'object') {
// just in case example was cached as json but used as non-json

View File

@ -3,7 +3,7 @@ import * as React from 'react';
import styled from '../../styled-components';
import { DropdownProps } from '../../common-elements';
import { MediaTypeModel } from '../../services/models';
import { ExampleModel, MediaTypeModel } from '../../services/models';
import { Markdown } from '../Markdown/Markdown';
import { Example } from './Example';
import { DropdownLabel, DropdownWrapper, NoSampleLabel } from './styled.elements';
@ -11,6 +11,9 @@ import { DropdownLabel, DropdownWrapper, NoSampleLabel } from './styled.elements
export interface PayloadSamplesProps {
mediaType: MediaTypeModel;
renderDropdown: (props: DropdownProps) => JSX.Element;
editable?: boolean;
value?: any;
onContentChange?: (value: any) => void;
}
interface MediaTypeSamplesState {
@ -18,15 +21,32 @@ interface MediaTypeSamplesState {
}
export class MediaTypeSamples extends React.Component<PayloadSamplesProps, MediaTypeSamplesState> {
state = {
constructor(props: PayloadSamplesProps) {
super(props);
this.state = {
activeIdx: 0,
};
if (this.props.editable && this.props.onContentChange) {
this.props.onContentChange(this.getExample().value);
}
}
switchMedia = ({ idx }) => {
this.setState({
activeIdx: idx,
});
};
getExample = () => {
const examples = this.props.mediaType.examples || {};
const examplesNames = Object.keys(examples);
return examples[examplesNames[this.state.activeIdx]];
};
render() {
const { editable = false, value, onContentChange } = this.props;
const { activeIdx } = this.state;
const examples = this.props.mediaType.examples || {};
const mimeType = this.props.mediaType.name;
@ -38,7 +58,7 @@ export class MediaTypeSamples extends React.Component<PayloadSamplesProps, Media
return noSample;
}
if (examplesNames.length > 1) {
if (!editable && examplesNames.length > 1) {
const options = examplesNames.map((name, idx) => {
return {
value: examples[name].summary || name,
@ -62,20 +82,51 @@ export class MediaTypeSamples extends React.Component<PayloadSamplesProps, Media
</DropdownWrapper>
<div>
{description && <Markdown source={description} />}
<Example example={example} mimeType={mimeType} />
{this.getExampleComponent(example, mimeType, editable, value, onContentChange)}
</div>
</SamplesWrapper>
);
} else {
const example = examples[examplesNames[0]];
return (
return editable ? (
<SamplesWrapper>
<DropdownLabel>Request body</DropdownLabel>
<div
style={{
backgroundColor: 'rgba(38, 50, 56, 0.4)',
borderColor: 'rgba(38, 50, 56, 0.5)',
}}
>
<div style={{ height: 8 }}></div>
{this.getExampleComponent(example, mimeType, editable, value, onContentChange)}
</div>
</SamplesWrapper>
) : (
<SamplesWrapper>
{example.description && <Markdown source={example.description} />}
<Example example={example} mimeType={mimeType} />
{this.getExampleComponent(example, mimeType, editable, value, onContentChange)}
</SamplesWrapper>
);
}
}
getExampleComponent = (
example: ExampleModel,
mimeType: string,
editable: boolean,
value: any,
onContentChange,
) => {
return (
<Example
example={example}
mimeType={mimeType}
editable={editable}
value={value}
onChange={onContentChange}
/>
);
};
}
const SamplesWrapper = styled.div`

View File

@ -0,0 +1,299 @@
import { observer } from 'mobx-react';
import * as React from 'react';
import axios from 'axios';
import { MediaTypeSamples } from './MediaTypeSamples';
import { MediaContentModel, OperationModel } from '../..';
import { DropdownOrLabel, JsonViewer, MediaTypesSwitch } from '..';
import {
DropdownLabel,
InvertedSimpleDropdown,
MimeLabel,
ParamBox,
ParamPrompt,
ParamInput,
ParamTable,
TryUrl,
TryBtn,
TryTitle,
TrySubtitle,
} from './styled.elements';
import { EmptyObject } from '../../types';
export interface TryLiveProps {
content?: MediaContentModel | null;
operation: OperationModel;
selectedServerUrl: string;
}
export interface TryLiveState {
url: { html: any[]; str: string };
response: any;
error: any;
body: any;
params: Record<string, string>;
}
@observer
export class Trylive extends React.Component<TryLiveProps, TryLiveState> {
constructor(props: TryLiveProps) {
super(props);
const { selectedServerUrl, operation } = this.props;
const initParams = this.getInitParams();
this.state = {
url: this.getParamString(selectedServerUrl + operation.path, initParams),
response: null,
error: null,
body: null,
params: initParams,
};
}
getInitParams = () => {
const { operation } = this.props;
const path = operation.path;
const paramRe = /\{([a-zA-Z_][a-zA-Z0-9_]*)\}/gi;
let match;
const params = {};
while ((match = paramRe.exec(path)) !== null) {
params[match[1]] = '';
}
return params;
};
getParamString = (str: string, params = {}) => {
const regex1 = RegExp('{[a-zA-Z_][a-zA-Z0-9_]*}', 'gi');
let count = 0;
let lastIndex = 0;
let outHtml: any[] = []; // url as html fragments
let outStr = ''; // url as a string (used for making requests)
let tmpStr = str;
let tmpArr;
while ((tmpArr = regex1.exec(tmpStr)) !== null) {
const match = tmpArr[0];
lastIndex = regex1.lastIndex;
const preParamStr = tmpStr.substr(0, lastIndex - match.length);
const paramValue = params[match.replace('{', '').replace('}', '')];
outHtml = outHtml.concat([
<React.Fragment key={count}>{preParamStr}</React.Fragment>,
<span key={count + 1}>{paramValue || match}</span>,
]);
outStr += preParamStr + paramValue;
tmpStr = tmpStr.slice(lastIndex);
count += 2;
}
if (tmpStr.length > 0) {
if (count === 0) {
outHtml = [<React.Fragment key={count}>{str}</React.Fragment>];
outStr = str;
} else {
outHtml = outHtml.concat([<React.Fragment key={count}>{tmpStr}</React.Fragment>]);
outStr += tmpStr;
}
}
return {
html: outHtml,
str: outStr,
};
};
onInputChange = e => {
e.preventDefault();
const name = e.target.name;
const value = e.target.value;
this.setState(
state => ({
...state,
params: {
...state.params,
[name]: value,
},
}),
() => this.updateUrl(),
);
};
updateUrl = () => {
const { selectedServerUrl, operation } = this.props;
this.setState(state => ({
...state,
url: this.getParamString(selectedServerUrl + operation.path, this.state.params),
}));
};
componentDidMount() {
this.updateUrl();
}
componentDidUpdate(prevProps) {
if (
this.props.selectedServerUrl &&
this.props.selectedServerUrl !== prevProps.selectedServerUrl
) {
this.updateUrl();
}
}
getTryLiveParams = () => {
return this.props.operation.tryLiveParams;
};
getVerb = () => {
return this.props.operation.httpVerb;
};
onTry = () => {
this.setState(
state => ({ ...state, response: null, error: null }),
() => {
this.doTry();
},
);
};
doTry = () => {
const body = this.state.body || {};
const apiAccessToken = this.getTryLiveParams().tryLiveAccessToken;
const headers = {
Accept: 'application/json',
'Content-Type': 'application/json;charset=UTF-8',
};
if (apiAccessToken) {
headers['Authorization'] = 'Bearer' + apiAccessToken;
}
const options: EmptyObject = {
url: this.state.url.str,
method: this.getVerb(),
headers,
data: body,
};
axios(options)
.then(response => {
this.setState({ response });
})
.catch(error => {
this.setState({ error });
});
};
onContentChange = (_value: any) => {
let value = {};
if (typeof _value === 'string') {
try {
value = JSON.parse(_value.split('\\n').join('').trim());
} catch (error) {}
} else {
value = _value;
}
this.setState({ body: value });
};
render() {
const { content: mimeContent } = this.props;
const { params, body, error, response, url } = this.state;
return (
<>
<div style={{ position: 'relative' }}>
<DropdownLabel>Request url</DropdownLabel>
<TryUrl>
<div>
<span>{url.html}</span>
</div>
<TryBtn onClick={this.onTry}>
<span>SEND</span>
</TryBtn>
</TryUrl>
</div>
{Object.keys(params).length > 0 && (
<ParamBox>
<DropdownLabel>Parameters</DropdownLabel>
<ParamTable>
<table>
<tbody>
{Object.keys(params).map(key => {
return (
<tr key={key}>
<td>
<ParamPrompt>{key}</ParamPrompt>
</td>
<td>
<ParamInput>
<input
name={key}
value={params[key]}
onChange={this.onInputChange}
type="text"
spellCheck={false}
/>
</ParamInput>
</td>
</tr>
);
})}
</tbody>
</table>
</ParamTable>
</ParamBox>
)}
{mimeContent && (
<div style={{ marginTop: 16 }}>
<MediaTypesSwitch
content={mimeContent}
renderDropdown={this.renderDropdown}
withLabel={true}
>
{mediaType => (
<MediaTypeSamples
key="samples"
mediaType={mediaType}
renderDropdown={this.renderDropdown}
editable={true}
value={body}
onContentChange={this.onContentChange}
/>
)}
</MediaTypesSwitch>
</div>
)}
{response && (
<div style={{ padding: 0 }}>
<TryTitle>Response</TryTitle>
Status: {response.status}
{response.data && <JsonViewer data={response.data} />}
</div>
)}
{error && (
<div style={{ padding: 0 }}>
<TryTitle>Error</TryTitle>
{error.message && (
<>
<TrySubtitle>Message</TrySubtitle>
<span style={{ color: '#e27a7a' }}>{error.message}</span>
</>
)}
{error.response && (
<>
<TrySubtitle>Response</TrySubtitle>
<JsonViewer data={error.response} />
</>
)}
</div>
)}
</>
);
}
private renderDropdown = props => {
return <DropdownOrLabel Label={MimeLabel} Dropdown={InvertedSimpleDropdown} {...props} />;
};
}

View File

@ -1,6 +1,6 @@
import { transparentize } from 'polished';
import styled from '../../styled-components';
import { StyledDropdown } from '../../common-elements';
import { StyledDropdown, RightPanelHeaderDiv } from '../../common-elements';
export const MimeLabel = styled.div`
padding: 0.9em;
@ -75,3 +75,133 @@ export const NoSampleLabel = styled.div`
font-size: 12px;
color: #ee807f;
`;
export const TryUrlTop = styled.div`
outline: 0;
display: flex;
align-items: center;
justify-content: space-between;
font-family: Courier, monospace;
width: 100%;
text-align: left;
border: 1px solid #ccc;
background: #fff;
word-break: break-all;
background-color: rgba(38, 50, 56, 0.4);
border-color: rgba(38, 50, 56, 0.5);
font-size: 1em;
border: none;
color: ${props => props.theme.colors.primary.main};
`;
export const TryUrl = styled(TryUrlTop)`
background-color: rgba(38, 50, 56, 0.4);
padding: 10px;
> div:nth-child(1) {
background: rgba(0, 0, 0, 0.2);
border-color: rgba(38, 50, 56, 0.5);
padding: 6px 8px;
border-radius: 3px;
width: calc(100% - 50px);
> span {
flex: 1;
font-family: Courier, monospace;
font-size: 0.95em;
background: transparent;
color: #ffffff !important;
outline: none;
border: none;
text-align: left;
> span {
color: #a0fbaa !important;
}
}
}
`;
export const TryBtn = styled.div`
flex: 0 0 auto;
margin: 5px 0 5px 8px;
font-size: 0.9em;
font-weight: 400;
line-height: 24px;
background: white;
border-radius: 2px;
width: 52px;
text-align: center;
text-transform: uppercase;
font-family: Montserrat, sans-serif;
cursor: pointer;
> span {
padding: 5px;
color: black !important;
margin: 0;
}
`;
export const TryTitle = styled(RightPanelHeaderDiv)`
font-size: 1.2em;
padding: 5px 0;
font-weight: 400;
`;
export const TrySubtitle = styled(RightPanelHeaderDiv)`
font-size: 1.1em;
padding: 4px 0;
font-weight: 300;
`;
export const ParamBox = styled(TryUrlTop)`
padding: 10px 0;
margin-top: 15px;
position: relative;
`;
export const ParamPrompt = styled.div`
padding: 4px 0;
font-weight: 300;
font-size: 0.9em;
line-height: 18px;
color: #ffffff !important;
`;
export const ParamInput = styled(TryUrlTop)`
max-width: 250px;
background-color: rgba(0, 0, 0, 0.2);
border-radius: 3px;
font-size: 0.9em;
> input {
padding: 0.5em;
width: 100%;
font-family: Courier, monospace;
background: transparent;
color: #a0fbaa !important;
outline: none;
border: none;
word-break: break-all;
}
`;
export const ParamTable = styled.div`
width: 100%;
background: none;
> table {
width: 100%;
tab-index: -1;
cell-padding: 1;
cell-spacing: 0;
td {
vertical-align: middle;
text-align: right;
padding: 5px 10px 0 10px;
}
td:nth-child(2) {
text-align: left;
max-width: 200px;
}
}
`;

View File

@ -1,7 +1,14 @@
import { observer } from 'mobx-react';
import * as React from 'react';
import { isPayloadSample, OperationModel, RedocNormalizedOptions } from '../../services';
import {
isPayloadSample,
isTrySample,
OperationModel,
RedocNormalizedOptions,
AppStore,
} from '../../services';
import { PayloadSamples } from '../PayloadSamples/PayloadSamples';
import { Trylive } from '../PayloadSamples/TryLive';
import { SourceCodeWithCopy } from '../SourceCode/SourceCode';
import { RightPanelHeader, Tab, TabList, TabPanel, Tabs } from '../../common-elements';
@ -10,6 +17,7 @@ import { l } from '../../services/Labels';
export interface RequestSamplesProps {
operation: OperationModel;
store?: AppStore;
}
@observer
@ -19,13 +27,14 @@ export class RequestSamples extends React.Component<RequestSamplesProps> {
operation: OperationModel;
render() {
const { operation } = this.props;
const { operation, store } = this.props;
const samples = operation.codeSamples;
const hasSamples = samples.length > 0;
const hideTabList = samples.length === 1 ? this.context.hideSingleRequestSampleTab : false;
return (
(hasSamples && (
const selectedServerUrl = store?.spec.selectedServerUrl || '';
return hasSamples ? (
<div>
<RightPanelHeader> {l('requestSamples')} </RightPanelHeader>
@ -43,6 +52,14 @@ export class RequestSamples extends React.Component<RequestSamplesProps> {
<div>
<PayloadSamples content={sample.requestBodyContent} />
</div>
) : isTrySample(sample) ? (
<div>
<Trylive
content={sample.requestBodyContent}
operation={operation}
selectedServerUrl={selectedServerUrl}
/>
</div>
) : (
<SourceCodeWithCopy lang={sample.lang} source={sample.source} />
)}
@ -50,8 +67,19 @@ export class RequestSamples extends React.Component<RequestSamplesProps> {
))}
</Tabs>
</div>
)) ||
null
) : (
<div>
<Tabs defaultIndex={0}>
<TabList hidden={false}>
<Tab key="try">Try</Tab>
</TabList>
<TabPanel>
<div>
<Trylive content={null} operation={operation} selectedServerUrl={selectedServerUrl} />
</div>
</TabPanel>
</Tabs>
</div>
);
}
}

View File

@ -34,3 +34,5 @@ export * from './StickySidebar/StickyResponsiveSidebar';
export * from './SearchBox/SearchBox';
export * from './SchemaDefinition/SchemaDefinition';
export * from './SourceCode/SourceCode';
export { default as JsonEditorWrapper } from './JsonEditor/JsonEditorWrapper';

View File

@ -36,6 +36,11 @@ export interface RedocRawOptions {
payloadSampleIdx?: number;
expandSingleSchemaField?: boolean | string;
// try live
tryLiveAccessToken?: string;
tryLiveSandboxServerIndex?: number;
tryLiveEditorTheme?: string;
unstable_ignoreMimeParameters?: boolean;
allowedMdComponents?: Record<string, MDXComponentMeta>;
@ -221,6 +226,11 @@ export class RedocNormalizedOptions {
payloadSampleIdx: number;
expandSingleSchemaField: boolean;
// try live
tryLiveAccessToken: string;
tryLiveSandboxServerIndex: number;
tryLiveEditorTheme: string;
/* tslint:disable-next-line */
unstable_ignoreMimeParameters: boolean;
allowedMdComponents: Record<string, MDXComponentMeta>;
@ -282,6 +292,14 @@ export class RedocNormalizedOptions {
this.payloadSampleIdx = RedocNormalizedOptions.normalizePayloadSampleIdx(raw.payloadSampleIdx);
this.expandSingleSchemaField = argValueToBoolean(raw.expandSingleSchemaField);
// try live
this.tryLiveAccessToken = raw.tryLiveAccessToken || '';
this.tryLiveSandboxServerIndex =
typeof raw.tryLiveSandboxServerIndex === 'number'
? raw.tryLiveSandboxServerIndex
: argValueToNumber(raw.tryLiveSandboxServerIndex) || 1;
this.tryLiveEditorTheme = raw.tryLiveAccessToken || '';
this.unstable_ignoreMimeParameters = argValueToBoolean(raw.unstable_ignoreMimeParameters);
this.allowedMdComponents = raw.allowedMdComponents || {};

View File

@ -1,4 +1,10 @@
import { OpenAPIExternalDocumentation, OpenAPIPath, OpenAPISpec, Referenced } from '../types';
import {
OpenAPIExternalDocumentation,
OpenAPIPath,
OpenAPIServer,
OpenAPISpec,
Referenced,
} from '../types';
import { ContentItemModel, MenuBuilder } from './MenuBuilder';
import { ApiInfoModel } from './models/ApiInfo';
@ -6,6 +12,7 @@ import { WebhookModel } from './models/Webhook';
import { SecuritySchemesModel } from './models/SecuritySchemes';
import { OpenAPIParser } from './OpenAPIParser';
import { RedocNormalizedOptions } from './RedocNormalizedOptions';
import { action, computed, observable, makeObservable } from 'mobx';
/**
* Store that contains all the specification related information in the form of tree
*/
@ -18,11 +25,18 @@ export class SpecStore {
securitySchemes: SecuritySchemesModel;
webhooks?: WebhookModel;
@observable
selectedServerIndex: number;
servers: OpenAPIServer[];
constructor(
spec: OpenAPISpec,
specUrl: string | undefined,
private options: RedocNormalizedOptions,
) {
makeObservable(this);
this.parser = new OpenAPIParser(spec, specUrl, options);
this.info = new ApiInfoModel(this.parser);
this.externalDocs = this.parser.spec.externalDocs;
@ -33,5 +47,26 @@ export class SpecStore {
...this.parser?.spec.webhooks,
};
this.webhooks = new WebhookModel(this.parser, options, webhookPath);
this.servers = spec.servers || [];
this.selectedServerIndex = this.getInitialServerIndex();
}
@action
setSelectedServerIndex = (index: number) => {
this.selectedServerIndex = index;
};
@computed
get selectedServerUrl() {
return this.servers[this.selectedServerIndex].url;
}
getInitialServerIndex = () => {
return this.options.tryLiveAccessToken
? this.options.tryLiveSandboxServerIndex === 0
? 1
: 0
: this.options.tryLiveSandboxServerIndex;
};
}

View File

@ -40,6 +40,17 @@ export function isPayloadSample(
return sample.lang === 'payload' && (sample as any).requestBodyContent;
}
export interface XTrySample {
lang: 'try';
label: string;
requestBodyContent: MediaContentModel;
source: string;
}
export function isTrySample(sample: XTrySample | OpenAPIXCodeSample): sample is XTrySample {
return sample.lang === 'try' && (sample as any).requestBodyContent;
}
let isCodeSamplesWarningPrinted = false;
/**
@ -189,7 +200,7 @@ export class OperationModel implements IMenuItem {
@memoize
get codeSamples() {
let samples: Array<OpenAPIXCodeSample | XPayloadSample> =
let samples: Array<OpenAPIXCodeSample | XPayloadSample | XTrySample> =
this.operationSpec['x-codeSamples'] || this.operationSpec['x-code-samples'] || [];
if (this.operationSpec['x-code-samples'] && !isCodeSamplesWarningPrinted) {
@ -209,6 +220,12 @@ export class OperationModel implements IMenuItem {
source: '',
requestBodyContent,
},
{
lang: 'try',
label: 'Try',
source: '',
requestBodyContent,
},
...samples.slice(insertInx),
];
}
@ -216,6 +233,15 @@ export class OperationModel implements IMenuItem {
return samples;
}
// try live
get tryLiveParams() {
return {
tryLiveAccessToken: this.options.tryLiveAccessToken,
tryLiveSandboxServerIndex: this.options.tryLiveSandboxServerIndex,
tryLiveEditorTheme: this.options.tryLiveEditorTheme,
};
}
@memoize
get parameters() {
const _parameters = mergeParams(

View File

@ -1,3 +1,4 @@
export * from './open-api';
export type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
export type EmptyObject = Record<string, unknown>;