redoc/demo/index.tsx

213 lines
5.4 KiB
TypeScript
Raw Permalink Normal View History

2018-03-13 13:58:22 +03:00
import * as React from 'react';
import { render } from 'react-dom';
import styled from 'styled-components';
2018-03-13 14:04:55 +03:00
import { resolve as urlResolve } from 'url';
import { RedocStandalone } from '../src';
2018-03-13 13:58:22 +03:00
import ComboBox from './ComboBox';
import FileInput from './components/FileInput';
2018-03-13 13:58:22 +03:00
const DEFAULT_SPEC = 'openapi.yaml';
const NEW_VERSION_SPEC = 'openapi-3-1.yaml';
2018-03-13 13:58:22 +03:00
const demos = [
{ value: NEW_VERSION_SPEC, label: 'Petstore OpenAPI 3.1' },
2018-03-13 13:58:22 +03:00
{ value: 'https://api.apis.guru/v2/specs/instagram.com/1.0.0/swagger.yaml', label: 'Instagram' },
{
2020-10-02 11:46:27 +03:00
value: 'https://api.apis.guru/v2/specs/googleapis.com/calendar/v3/openapi.yaml',
label: 'Google Calendar',
},
2021-07-01 12:53:49 +03:00
{ value: 'https://api.apis.guru/v2/specs/slack.com/1.7.0/openapi.yaml', label: 'Slack' },
{ value: 'https://api.apis.guru/v2/specs/zoom.us/2.0.0/openapi.yaml', label: 'Zoom.us' },
2020-05-09 14:31:25 +03:00
{ value: 'https://docs.graphhopper.com/openapi.json', label: 'GraphHopper' },
2018-03-13 13:58:22 +03:00
];
class DemoApp extends React.Component<
{},
{ spec: object | undefined; specUrl: string; dropdownOpen: boolean; cors: boolean }
2018-03-22 19:22:11 +03:00
> {
2018-03-13 13:58:22 +03:00
constructor(props) {
super(props);
let parts = window.location.search.match(/url=([^&]+)/);
let url = DEFAULT_SPEC;
if (parts && parts.length > 1) {
url = decodeURIComponent(parts[1]);
}
parts = window.location.search.match(/[?&]nocors(&|#|$)/);
let cors = true;
if (parts && parts.length > 1) {
cors = false;
}
this.state = {
spec: undefined,
2018-03-13 13:58:22 +03:00
specUrl: url,
dropdownOpen: false,
cors,
};
}
handleUploadFile = (spec: object) => {
this.setState({
spec,
specUrl: '',
});
};
2018-03-13 13:58:22 +03:00
handleChange = (url: string) => {
if (url === NEW_VERSION_SPEC) {
this.setState({ cors: false });
0;
}
2018-03-13 13:58:22 +03:00
this.setState({
specUrl: url,
});
window.history.pushState(
undefined,
'',
updateQueryStringParameter(location.search, 'url', url),
);
};
toggleCors = (e: React.ChangeEvent<HTMLInputElement>) => {
const cors = e.currentTarget.checked;
this.setState({
2018-03-13 14:04:55 +03:00
cors,
2018-03-13 13:58:22 +03:00
});
window.history.pushState(
undefined,
'',
updateQueryStringParameter(location.search, 'nocors', cors ? undefined : ''),
);
};
render() {
const { specUrl, cors } = this.state;
let proxiedUrl = specUrl;
if (specUrl !== DEFAULT_SPEC) {
proxiedUrl = cors
2021-07-01 12:31:20 +03:00
? '\\\\cors.redoc.ly/' + urlResolve(window.location.href, specUrl)
2018-03-13 13:58:22 +03:00
: specUrl;
}
return (
<>
<Heading>
<a href=".">
<Logo
src="https://github.com/Redocly/redoc/raw/master/docs/images/redoc-logo.png"
alt="Redoc logo"
/>
2018-03-13 13:58:22 +03:00
</a>
<ControlsContainer>
<FileInput onUpload={this.handleUploadFile} />
2018-03-13 13:58:22 +03:00
<ComboBox
placeholder={'URL to a spec to try'}
options={demos}
onChange={this.handleChange}
value={specUrl === DEFAULT_SPEC ? '' : specUrl}
/>
<CorsCheckbox title="Use CORS proxy">
<input id="cors_checkbox" type="checkbox" onChange={this.toggleCors} checked={cors} />
<label htmlFor="cors_checkbox">CORS</label>
</CorsCheckbox>
</ControlsContainer>
<iframe
src="https://ghbtns.com/github-btn.html?user=Redocly&amp;repo=redoc&amp;type=star&amp;count=true&amp;size=large"
2018-03-13 13:58:22 +03:00
frameBorder="0"
scrolling="0"
width="160px"
2018-03-13 13:58:22 +03:00
height="30px"
/>
</Heading>
2020-04-27 19:58:03 +03:00
<RedocStandalone
spec={this.state.spec}
2020-04-27 19:58:03 +03:00
specUrl={proxiedUrl}
options={{ scrollYOffset: 'nav', untrustedSpec: true }}
/>
2018-03-13 13:58:22 +03:00
</>
);
}
}
/* ====== Styled components ====== */
const ControlsContainer = styled.div`
display: flex;
justify-content: center;
flex: 1;
margin: 0 15px;
align-items: center;
`;
const CorsCheckbox = styled.div`
margin-left: 10px;
2018-03-22 19:22:11 +03:00
white-space: nowrap;
2018-03-13 13:58:22 +03:00
label {
font-size: 13px;
}
2018-03-22 19:22:11 +03:00
@media screen and (max-width: 550px) {
display: none;
}
2018-03-13 13:58:22 +03:00
`;
const Heading = styled.nav`
position: sticky;
top: 0;
2018-03-22 19:22:11 +03:00
width: 100%;
2018-03-13 13:58:22 +03:00
height: 50px;
box-sizing: border-box;
background: white;
border-bottom: 1px solid #cccccc;
z-index: 10;
padding: 5px;
display: flex;
align-items: center;
font-family: Roboto, sans-serif;
2018-03-13 13:58:22 +03:00
`;
const Logo = styled.img`
height: 40px;
width: 124px;
display: inline-block;
margin-right: 15px;
2018-03-22 19:22:11 +03:00
@media screen and (max-width: 950px) {
display: none;
}
2018-03-13 13:58:22 +03:00
`;
render(<DemoApp />, document.getElementById('container'));
/* ====== Helpers ====== */
function updateQueryStringParameter(uri, key, value) {
const keyValue = value === '' ? key : key + '=' + value;
2018-03-13 14:04:55 +03:00
const re = new RegExp('([?|&])' + key + '=?.*?(&|#|$)', 'i');
2018-03-13 13:58:22 +03:00
if (uri.match(re)) {
if (value !== undefined) {
return uri.replace(re, '$1' + keyValue + '$2');
} else {
return uri.replace(re, (_, separator: string, rest: string) => {
if (rest.startsWith('&')) {
rest = rest.substring(1);
}
return separator === '&' ? rest : separator + rest;
});
}
} else {
if (value === undefined) {
return uri;
}
2018-03-13 14:04:55 +03:00
let hash = '';
2018-03-13 13:58:22 +03:00
if (uri.indexOf('#') !== -1) {
hash = uri.replace(/.*#/, '#');
uri = uri.replace(/#.*/, '');
}
2018-03-13 14:04:55 +03:00
const separator = uri.indexOf('?') !== -1 ? '&' : '?';
2018-03-13 13:58:22 +03:00
return uri + separator + keyValue + hash;
}
}