import { SVGInjector } from '@tanem/svg-injector' ; import * as PropTypes from 'prop-types' ; import * as React from 'react' ; import shallowDiffers from './shallow-differs' ; import { Props , State , WrapperType } from './types' ; const svgNamespace = 'http://www.w3.org/2000/svg' ; const xlinkNamespace = 'http://www.w3.org/1999/xlink' ; export class ReactSVG extends React . Component < Props , State > { static defaultProps = { afterInjection : ( ) => undefined , beforeInjection : ( ) => undefined , evalScripts : 'never' , fallback : null , httpRequestWithCredentials : false , loading : null , renumerateIRIElements : true , useRequestCache : true , wrapper : 'div' , } ; static propTypes = { afterInjection : PropTypes . func , beforeInjection : PropTypes . func , evalScripts : PropTypes . oneOf ( [ 'always' , 'once' , 'never' ] ) , fallback : PropTypes . oneOfType ( [ PropTypes . func , PropTypes . object , PropTypes . string , ] ) , httpRequestWithCredentials : PropTypes . bool , loading : PropTypes . oneOfType ( [ PropTypes . func , PropTypes . object , PropTypes . string , ] ) , renumerateIRIElements : PropTypes . bool , src : PropTypes . string . isRequired , useRequestCache : PropTypes . bool , wrapper : PropTypes . oneOf ( [ 'div' , 'span' , 'svg' ] ) , } ; initialState = { hasError : false , isLoading : true , } ; state = this . initialState ; _isMounted = false ; reactWrapper ? : WrapperType | null ; nonReactWrapper ? : WrapperType | null ; refCallback = ( reactWrapper ) => { this . reactWrapper = reactWrapper ; } ; renderSVG ( ) { if ( this . reactWrapper instanceof Node ) { const { beforeInjection , evalScripts , httpRequestWithCredentials , renumerateIRIElements , src , useRequestCache , } = this . props ; const afterInjection = this . props . afterInjection ! ; const wrapper = this . props . wrapper ! ; let nonReactWrapper ; let nonReactTarget ; if ( wrapper === 'svg' ) { nonReactWrapper = document . createElementNS ( svgNamespace , wrapper ) ; nonReactWrapper . setAttribute ( 'xmlns' , svgNamespace ) ; nonReactWrapper . setAttribute ( 'xmlns:xlink' , xlinkNamespace ) ; nonReactTarget = document . createElementNS ( svgNamespace , wrapper ) ; } else { nonReactWrapper = document . createElement ( wrapper ) ; nonReactTarget = document . createElement ( wrapper ) ; } nonReactWrapper . appendChild ( nonReactTarget ) ; nonReactTarget . dataset . src = src ; this . nonReactWrapper = this . reactWrapper . appendChild ( nonReactWrapper ) ; const afterEach = ( error , svg ? ) => { if ( error ) { this . removeSVG ( ) ; if ( ! this . _isMounted ) { afterInjection ( error ) ; return ; } } if ( this . _isMounted ) { this . setState ( ( ) => ( { hasError : ! ! error , isLoading : false , } ) , ( ) => { afterInjection ( error , svg ) ; } ) ; } } ; SVGInjector ( nonReactTarget , { afterEach , beforeEach : beforeInjection , cacheRequests : useRequestCache , evalScripts , httpRequestWithCredentials , renumerateIRIElements , } ) ; } } removeSVG ( ) { if ( this . nonReactWrapper ?. parentNode ) { this . nonReactWrapper . parentNode . removeChild ( this . nonReactWrapper ) ; this . nonReactWrapper = null ; } } componentDidMount ( ) { this . _isMounted = true ; this . renderSVG ( ) ; } componentDidUpdate ( prevProps ) { if ( shallowDiffers ( prevProps , this . props ) ) { this . setState ( ( ) => this . initialState , ( ) => { this . removeSVG ( ) ; this . renderSVG ( ) ; } ) ; } } componentWillUnmount ( ) { this . _isMounted = false ; this . removeSVG ( ) ; } render ( ) { const { afterInjection , beforeInjection , evalScripts , fallback : Fallback , httpRequestWithCredentials , loading : Loading , renumerateIRIElements , src , useRequestCache , wrapper , ... rest } = this . props ; const Wrapper = wrapper ! ; return ( < Wrapper { ... rest } ref = { this . refCallback } { ... ( wrapper === 'svg' ? { xmlns : svgNamespace , xmlnsXlink : xlinkNamespace , } : { } ) } > { this . state . isLoading && Loading && < Loading />} { this . state . hasError && Fallback && < Fallback />} < /Wrapper>); } }