import * as React from 'react' ; import SVGContext from './context' ; const DEFINE = 'defs' ; const OMIT_SVG_ELEMENTS = [ 'title' , 'desc' , 'defs' ] ; function replaceColor ( props , theme ) { if ( ! theme ) { return props ; } let newProps = props ; if ( props . fill ) { newProps = { ... newProps } ; newProps . fill = theme [ props . fill ] || props . fill ; } return newProps ; } export interface SVGProps extends Omit < React . SVGAttributes < any > , 'path' > { tagName : string ; path ? : number [ ] ; } function SVG ( { tagName : Component , path , ... rest } ) { const { chipManger , theme } = React . useContext ( SVGContext ) ; const chip = chipManger . getChip ( path ) ; if ( chip ) { const props = { } ; return < Component { ... replaceColor ( rest , theme ) } { ... props } />; } return < Component { ... replaceColor ( rest , theme ) } / > ; } function toNumber ( unit ) { return Number ( unit . replace ( /[^\d.]/g , '' ) ) || 0 ; } export interface ParseOption { debug ? : boolean ; name ? : string ; width ? : number ; height ? : number ; } let uuid = 0 ; function getUUID ( ) { const rnd = `${ Math . random ( ) . toFixed ( 10 ) }` . replace ( '.' , '' ) ; const id = `HITU_${ rnd }_${ uuid }` ; uuid += 1 ; return id ; } function camelCase ( str ) { let out = '' ; for ( let i = 0 ; i < str . length ; i += 1 ) { const char = str [ i ] ; if ( char === '-' || char === ':' ) { i += 1 ; const next = str [ i ] ; out += next . toUpperCase ( ) ; } else { out += char ; } } return out ; } function getAttributes ( dom ) { const { attributes } = dom ; const attrs = { } ; for ( let i = 0 ; i < attributes . length ; i += 1 ) { const attr = attributes [ i ] ; const name = camelCase ( attr . name ) ; attrs [ name ] = attr . value ; } return attrs ; } function getMatchId ( txt ) { const match = txt . match ( /#([^)]+)/ ) ; return match && match [ 1 ] ; } SVG . parse = ( text , { debug , name , width , height } = { } ) : React . ReactElement => { const id = name || getUUID ( ) ; function getId ( originId ) { return `${ id }_${ originId }` ; } const fragment = document . createDocumentFragment ( ) ; const container = document . createElement ( 'div' ) ; fragment . appendChild ( container ) ; container . innerHTML = text ; const svg = fragment . querySelector ( 'svg' ) ! ; let defsEle = null ; const defElements = [ ] ; const svgChildren = Array . from ( svg . children ) . filter ( node => { if ( node . tagName === DEFINE ) { defsEle = node as SVGDefsElement ; } return ! OMIT_SVG_ELEMENTS . includes ( node . tagName ) ; } ) ; if ( defsEle ) { Array . from ( defsEle ! . children ) . forEach ( ( def , index ) => { const attrs = getAttributes ( def ) ; attrs . id = getId ( attrs . id ) ; defElements . push ( < SVG key = { index } { ... attrs } tagName = { def . tagName } / > ) ; } ) ; } function dig ( children , parentPath = [ ] ) { return children . map ( ( node , index ) => { const { tagName } = node ; const attrs = getAttributes ( node ) ; if ( tagName === 'mask' ) { attrs . id = getId ( node . id ) ; } else if ( tagName === 'use' ) { const matchId = getMatchId ( attrs . xlinkHref ) ; if ( matchId ) { attrs . xlinkHref = attrs . xlinkHref . replace ( matchId , getId ( matchId ) ) ; } } if ( attrs . mask ) { const matchId = getMatchId ( attrs . mask ) ; if ( matchId ) { attrs . mask = attrs . mask . replace ( matchId , getId ( matchId ) ) ; } } const myPath = [ ... parentPath , index ] ; return ( < SVG { ... attrs } tagName = { tagName } key = { index } path = { myPath } > { dig ( Array . from ( node . children ) , myPath ) } < /SVG>); } ) ; } if ( debug ) { container . className = 'hitu-svg-debug' ; document . body . appendChild ( fragment ) ; ( window as any ) . HITU_SVG_DEBUG = svg ; } const svgAttrs = getAttributes ( svg ) ; const svgWidth = width || toNumber ( svgAttrs . width ) ; const svgHeight = height || toNumber ( svgAttrs . height ) ; return ( < SVG viewBox = { `0 0 ${ svgWidth } ${ svgHeight }` } { ... svgAttrs } width = { svgWidth } height = { svgHeight } tagName = "svg" > { [ < defs key = "defs" > { defElements } < /defs>, ...dig(svgChildren)]} < /SVG>); } ; export default SVG ; export function getColors ( text ) { const colors = text . match ( /#[a-fA-F\d]{6}/g ) || [ ] ; return colors ; }