mirror of
				https://github.com/Redocly/redoc.git
				synced 2025-11-04 01:37:32 +03:00 
			
		
		
		
	Implement options and scrollYOffset option
This commit is contained in:
		
							parent
							
								
									6fbd47b340
								
							
						
					
					
						commit
						c2f82cdc8b
					
				| 
						 | 
					@ -3,11 +3,11 @@ import { render } from 'react-dom';
 | 
				
			||||||
import { AppContainer } from 'react-hot-loader';
 | 
					import { AppContainer } from 'react-hot-loader';
 | 
				
			||||||
// import DevTools from 'mobx-react-devtools';
 | 
					// import DevTools from 'mobx-react-devtools';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { Redoc } from '../../src/components/Redoc/Redoc';
 | 
					import { Redoc, RedocProps } from '../../src/components/Redoc/Redoc';
 | 
				
			||||||
import { AppStore } from '../../src/services/AppStore';
 | 
					import { AppStore } from '../../src/services/AppStore';
 | 
				
			||||||
import { loadAndBundleSpec } from '../../src/utils/loadAndBundleSpec';
 | 
					import { loadAndBundleSpec } from '../../src/utils/loadAndBundleSpec';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const renderRoot = (Component: typeof Redoc, props: { store: AppStore }) =>
 | 
					const renderRoot = (Component: typeof Redoc, props: RedocProps) =>
 | 
				
			||||||
  render(
 | 
					  render(
 | 
				
			||||||
    <div>
 | 
					    <div>
 | 
				
			||||||
      <AppContainer>
 | 
					      <AppContainer>
 | 
				
			||||||
| 
						 | 
					@ -23,11 +23,12 @@ const swagger = window.location.search.indexOf('swagger') > -1; //compatibility
 | 
				
			||||||
const specUrl = swagger ? 'swagger.yaml' : big ? 'big-openapi.json' : 'openapi.yaml';
 | 
					const specUrl = swagger ? 'swagger.yaml' : big ? 'big-openapi.json' : 'openapi.yaml';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let store;
 | 
					let store;
 | 
				
			||||||
 | 
					const options = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function init() {
 | 
					async function init() {
 | 
				
			||||||
  const spec = await loadAndBundleSpec(specUrl);
 | 
					  const spec = await loadAndBundleSpec(specUrl);
 | 
				
			||||||
  store = new AppStore(spec, specUrl);
 | 
					  store = new AppStore(spec, specUrl);
 | 
				
			||||||
  renderRoot(Redoc, { store: store });
 | 
					  renderRoot(Redoc, { store: store, options });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
init();
 | 
					init();
 | 
				
			||||||
| 
						 | 
					@ -42,7 +43,7 @@ if (module.hot) {
 | 
				
			||||||
      store = AppStore.fromJS(state);
 | 
					      store = AppStore.fromJS(state);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    renderRoot(Redoc, { store: store });
 | 
					    renderRoot(Redoc, { store: store, options });
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  module.hot.accept(['../../src/components/Redoc/Redoc'], reload());
 | 
					  module.hot.accept(['../../src/components/Redoc/Redoc'], reload());
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -69,6 +69,7 @@
 | 
				
			||||||
    "webpack-node-externals": "^1.6.0"
 | 
					    "webpack-node-externals": "^1.6.0"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "dependencies": {
 | 
					  "dependencies": {
 | 
				
			||||||
 | 
					    "@types/prop-types": "^15.5.2",
 | 
				
			||||||
    "decko": "^1.2.0",
 | 
					    "decko": "^1.2.0",
 | 
				
			||||||
    "eventemitter3": "^2.0.3",
 | 
					    "eventemitter3": "^2.0.3",
 | 
				
			||||||
    "json-pointer": "^0.6.0",
 | 
					    "json-pointer": "^0.6.0",
 | 
				
			||||||
| 
						 | 
					@ -77,6 +78,7 @@
 | 
				
			||||||
    "mobx-react": "^4.3.3",
 | 
					    "mobx-react": "^4.3.3",
 | 
				
			||||||
    "openapi-sampler": "^1.0.0-beta.1",
 | 
					    "openapi-sampler": "^1.0.0-beta.1",
 | 
				
			||||||
    "prismjs": "^1.8.1",
 | 
					    "prismjs": "^1.8.1",
 | 
				
			||||||
 | 
					    "prop-types": "^15.6.0",
 | 
				
			||||||
    "react": "^16.0.0",
 | 
					    "react": "^16.0.0",
 | 
				
			||||||
    "react-dom": "^16.0.0",
 | 
					    "react-dom": "^16.0.0",
 | 
				
			||||||
    "react-dropdown": "^1.3.0",
 | 
					    "react-dropdown": "^1.3.0",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										40
									
								
								src/components/OptionsProvider.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								src/components/OptionsProvider.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,40 @@
 | 
				
			||||||
 | 
					import * as React from 'react';
 | 
				
			||||||
 | 
					import * as PropTypes from 'prop-types';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { RedocNormalizedOptions } from '../services/RedocNormalizedOptions';
 | 
				
			||||||
 | 
					import { ThemeInterface } from '../theme';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface RedocRawOptions {
 | 
				
			||||||
 | 
					  theme?: ThemeInterface;
 | 
				
			||||||
 | 
					  scrollYOffset?: number | string | Function;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface OptionsProviderProps {
 | 
				
			||||||
 | 
					  options: RedocRawOptions;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class OptionsProvider extends React.Component<OptionsProviderProps> {
 | 
				
			||||||
 | 
					  static childContextTypes = {
 | 
				
			||||||
 | 
					    redocOptions: PropTypes.object.isRequired,
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  getChildContext() {
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      redocOptions: new RedocNormalizedOptions(this.props.options),
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return React.Children.only(this.props.children);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class ComponentWithOptions<P = {}, S = {}> extends React.Component<P, S> {
 | 
				
			||||||
 | 
					  static contextTypes = {
 | 
				
			||||||
 | 
					    redocOptions: PropTypes.object,
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  get options(): RedocNormalizedOptions {
 | 
				
			||||||
 | 
					    return this.context.redocOptions || {};
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,48 +1,41 @@
 | 
				
			||||||
import * as React from 'react';
 | 
					import * as React from 'react';
 | 
				
			||||||
import Stickyfill from 'stickyfill';
 | 
					import * as PropTypes from 'prop-types';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { ThemeProvider } from '../../styled-components';
 | 
					import { ThemeProvider } from '../../styled-components';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { ApiInfo } from '../ApiInfo/ApiInfo';
 | 
					import { ApiInfo } from '../ApiInfo/ApiInfo';
 | 
				
			||||||
import { RedocWrap, StickySidebar, ApiContent } from './elements';
 | 
					import { RedocWrap, ApiContent } from './elements';
 | 
				
			||||||
import { ApiLogo } from '../ApiLogo/ApiLogo';
 | 
					import { ApiLogo } from '../ApiLogo/ApiLogo';
 | 
				
			||||||
import { SideMenu } from '../SideMenu/SideMenu';
 | 
					import { SideMenu } from '../SideMenu/SideMenu';
 | 
				
			||||||
import { ContentItems } from '../ContentItems/ContentItems';
 | 
					import { ContentItems } from '../ContentItems/ContentItems';
 | 
				
			||||||
import { AppStore } from '../../services';
 | 
					import { AppStore } from '../../services';
 | 
				
			||||||
 | 
					import { OptionsProvider, RedocRawOptions } from '../OptionsProvider';
 | 
				
			||||||
 | 
					import { StickySidebar } from '../StickySidebar/StickySidebar';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import defaultTheme, { ThemeInterface } from '../../theme';
 | 
					import defaultTheme from '../../theme';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface RedocProps {
 | 
					export interface RedocProps {
 | 
				
			||||||
  store: AppStore;
 | 
					  store: AppStore;
 | 
				
			||||||
  options?: {
 | 
					  options?: RedocRawOptions;
 | 
				
			||||||
    theme?: ThemeInterface;
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const stickyfill = Stickyfill();
 | 
					 | 
				
			||||||
export class Redoc extends React.Component<RedocProps> {
 | 
					export class Redoc extends React.Component<RedocProps> {
 | 
				
			||||||
  stickyElement: Element;
 | 
					  static propTypes = {
 | 
				
			||||||
 | 
					    store: PropTypes.instanceOf(AppStore).isRequired,
 | 
				
			||||||
 | 
					    options: PropTypes.object,
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  componentDidMount() {
 | 
					  componentDidMount() {
 | 
				
			||||||
    this.props.store.menu.updateOnHash();
 | 
					    this.props.store.menu.updateOnHash();
 | 
				
			||||||
    stickyfill.add(this.stickyElement);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  componentWillUnmount() {
 | 
					 | 
				
			||||||
    stickyfill.remove(this.stickyElement);
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  render() {
 | 
					  render() {
 | 
				
			||||||
    const { store: { spec, menu }, options = {} } = this.props;
 | 
					    const { store: { spec, menu }, options = {} } = this.props;
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
      <ThemeProvider theme={{ ...options.theme, ...defaultTheme }}>
 | 
					      <ThemeProvider theme={{ ...options.theme, ...defaultTheme }}>
 | 
				
			||||||
 | 
					        <OptionsProvider options={options}>
 | 
				
			||||||
          <RedocWrap className="redoc-wrap">
 | 
					          <RedocWrap className="redoc-wrap">
 | 
				
			||||||
          <StickySidebar
 | 
					            <StickySidebar className="menu-content">
 | 
				
			||||||
            className="menu-content"
 | 
					 | 
				
			||||||
            innerRef={el => {
 | 
					 | 
				
			||||||
              this.stickyElement = el;
 | 
					 | 
				
			||||||
            }}
 | 
					 | 
				
			||||||
          >
 | 
					 | 
				
			||||||
              <ApiLogo info={spec.info} />
 | 
					              <ApiLogo info={spec.info} />
 | 
				
			||||||
              <SideMenu menu={menu} />
 | 
					              <SideMenu menu={menu} />
 | 
				
			||||||
            </StickySidebar>
 | 
					            </StickySidebar>
 | 
				
			||||||
| 
						 | 
					@ -51,6 +44,7 @@ export class Redoc extends React.Component<RedocProps> {
 | 
				
			||||||
              <ContentItems items={menu.items as any} />
 | 
					              <ContentItems items={menu.items as any} />
 | 
				
			||||||
            </ApiContent>
 | 
					            </ApiContent>
 | 
				
			||||||
          </RedocWrap>
 | 
					          </RedocWrap>
 | 
				
			||||||
 | 
					        </OptionsProvider>
 | 
				
			||||||
      </ThemeProvider>
 | 
					      </ThemeProvider>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -33,21 +33,6 @@ export const RedocWrap = styled.div`
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const StickySidebar = styled.div`
 | 
					 | 
				
			||||||
  width: ${props => props.theme.menu.width};
 | 
					 | 
				
			||||||
  background-color: ${props => props.theme.menu.backgroundColor};
 | 
					 | 
				
			||||||
  overflow: hidden;
 | 
					 | 
				
			||||||
  display: flex;
 | 
					 | 
				
			||||||
  flex-direction: column;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  transform: translateZ(0);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  height: 100vh;
 | 
					 | 
				
			||||||
  position: sticky;
 | 
					 | 
				
			||||||
  position: -webkit-sticky;
 | 
					 | 
				
			||||||
  top: 0;
 | 
					 | 
				
			||||||
`;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const ApiContent = styled.div`
 | 
					export const ApiContent = styled.div`
 | 
				
			||||||
  z-index: 10;
 | 
					  z-index: 10;
 | 
				
			||||||
  position: relative;
 | 
					  position: relative;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										68
									
								
								src/components/StickySidebar/StickySidebar.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								src/components/StickySidebar/StickySidebar.tsx
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,68 @@
 | 
				
			||||||
 | 
					import * as React from 'react';
 | 
				
			||||||
 | 
					import Stickyfill from 'stickyfill';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { ComponentWithOptions, RedocRawOptions } from '../OptionsProvider';
 | 
				
			||||||
 | 
					import { RedocNormalizedOptions } from '../../services/RedocNormalizedOptions';
 | 
				
			||||||
 | 
					import styled from '../../styled-components';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface StickySidebarProps {
 | 
				
			||||||
 | 
					  className?: string;
 | 
				
			||||||
 | 
					  scrollYOffset?: RedocRawOptions['scrollYOffset']; // passed directly or via context
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const stickyfill = Stickyfill();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const StyledStickySidebar = styled.div`
 | 
				
			||||||
 | 
					  width: ${props => props.theme.menu.width};
 | 
				
			||||||
 | 
					  background-color: ${props => props.theme.menu.backgroundColor};
 | 
				
			||||||
 | 
					  overflow: hidden;
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  flex-direction: column;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  transform: translateZ(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  height: 100vh;
 | 
				
			||||||
 | 
					  position: sticky;
 | 
				
			||||||
 | 
					  position: -webkit-sticky;
 | 
				
			||||||
 | 
					  top: 0;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class StickySidebar extends ComponentWithOptions<StickySidebarProps> {
 | 
				
			||||||
 | 
					  stickyElement: Element;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  componentDidMount() {
 | 
				
			||||||
 | 
					    stickyfill.add(this.refs['sticky-children']);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  componentWillUnmount() {
 | 
				
			||||||
 | 
					    stickyfill.remove(this.refs['sticky-children']);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  get scrollYOffset() {
 | 
				
			||||||
 | 
					    let top;
 | 
				
			||||||
 | 
					    if (this.props.scrollYOffset !== undefined) {
 | 
				
			||||||
 | 
					      top = RedocNormalizedOptions.normalizeScrollYOffset(this.props.scrollYOffset)();
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      top = this.options.scrollYOffset();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return top + 'px';
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    let top = this.scrollYOffset;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const height = `calc(100vh - ${top})`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <StyledStickySidebar
 | 
				
			||||||
 | 
					        className={this.props.className}
 | 
				
			||||||
 | 
					        style={{ top, height }}
 | 
				
			||||||
 | 
					        innerRef={el => {
 | 
				
			||||||
 | 
					          this.stickyElement = el;
 | 
				
			||||||
 | 
					        }}
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        {this.props.children}
 | 
				
			||||||
 | 
					      </StyledStickySidebar>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,5 @@
 | 
				
			||||||
import { Component } from 'react';
 | 
					import { Component } from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { AppStore } from '../services/';
 | 
					import { AppStore } from '../services/';
 | 
				
			||||||
import { loadAndBundleSpec } from '../utils';
 | 
					import { loadAndBundleSpec } from '../utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										44
									
								
								src/services/RedocNormalizedOptions.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/services/RedocNormalizedOptions.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,44 @@
 | 
				
			||||||
 | 
					import { RedocRawOptions } from '../components/OptionsProvider';
 | 
				
			||||||
 | 
					import { ThemeInterface } from '../theme';
 | 
				
			||||||
 | 
					import { isNumeric } from '../utils/helpers';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class RedocNormalizedOptions {
 | 
				
			||||||
 | 
					  theme: ThemeInterface;
 | 
				
			||||||
 | 
					  scrollYOffset: () => number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  constructor(raw: RedocRawOptions) {
 | 
				
			||||||
 | 
					    this.scrollYOffset = RedocNormalizedOptions.normalizeScrollYOffset(raw.scrollYOffset);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static normalizeScrollYOffset(value: RedocRawOptions['scrollYOffset']): () => number {
 | 
				
			||||||
 | 
					    // just number is not valid selector and leads to crash so checking if isNumeric here
 | 
				
			||||||
 | 
					    if (typeof value === 'string' && !isNumeric(value)) {
 | 
				
			||||||
 | 
					      const el = document.querySelector(value);
 | 
				
			||||||
 | 
					      if (!el) {
 | 
				
			||||||
 | 
					        console.warn(
 | 
				
			||||||
 | 
					          'scrollYOffset value is a selector to non-existing element. Using offset 0 by default',
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      const bottom = (el && el.getBoundingClientRect().bottom) || 0;
 | 
				
			||||||
 | 
					      return () => bottom;
 | 
				
			||||||
 | 
					    } else if (typeof value === 'number' || isNumeric(value)) {
 | 
				
			||||||
 | 
					      return () => (typeof value === 'number' ? value : parseFloat(value));
 | 
				
			||||||
 | 
					    } else if (typeof value === 'function') {
 | 
				
			||||||
 | 
					      return () => {
 | 
				
			||||||
 | 
					        const res = value();
 | 
				
			||||||
 | 
					        if (typeof res !== 'number') {
 | 
				
			||||||
 | 
					          console.warn(
 | 
				
			||||||
 | 
					            `scrollYOffset should return number but returned value "${res}" of type ${typeof res}`,
 | 
				
			||||||
 | 
					          );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return res;
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					    } else if (value !== undefined) {
 | 
				
			||||||
 | 
					      console.warn(
 | 
				
			||||||
 | 
					        'Wrong value for scrollYOffset ReDoc option: should be string, number or function',
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return () => 0;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -63,3 +63,7 @@ export function stripTrailingSlash(path: string): string {
 | 
				
			||||||
export function isAbsolutePath(path: string): boolean {
 | 
					export function isAbsolutePath(path: string): boolean {
 | 
				
			||||||
  return /^(?:[a-z]+:)?/i.test(path);
 | 
					  return /^(?:[a-z]+:)?/i.test(path);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function isNumeric(n: any): n is Number {
 | 
				
			||||||
 | 
					  return !isNaN(parseFloat(n)) && isFinite(n);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -67,6 +67,10 @@
 | 
				
			||||||
  version "1.6.5"
 | 
					  version "1.6.5"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/@types/prismjs/-/prismjs-1.6.5.tgz#e222615538ea2df248c72512e1faf346af2640d6"
 | 
					  resolved "https://registry.yarnpkg.com/@types/prismjs/-/prismjs-1.6.5.tgz#e222615538ea2df248c72512e1faf346af2640d6"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"@types/prop-types@^15.5.2":
 | 
				
			||||||
 | 
					  version "15.5.2"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.5.2.tgz#3c6b8dceb2906cc87fe4358e809f9d20c8d59be1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
"@types/react-dom@^16.0.0":
 | 
					"@types/react-dom@^16.0.0":
 | 
				
			||||||
  version "16.0.3"
 | 
					  version "16.0.3"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.0.3.tgz#8accad7eabdab4cca3e1a56f5ccb57de2da0ff64"
 | 
					  resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.0.3.tgz#8accad7eabdab4cca3e1a56f5ccb57de2da0ff64"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user