|
1 | | -import React, { useRef, useEffect } from 'react'; |
| 1 | +import React, { useRef, useEffect, useMemo } from 'react'; |
2 | 2 | import PropTypes from 'prop-types'; |
3 | 3 |
|
4 | 4 | const InlineSVG = ({ src, ...otherProps }) => { |
5 | | - return ( |
6 | | - <div |
7 | | - // eslint-disable-next-line react/no-danger |
8 | | - dangerouslySetInnerHTML={{ __html: src }} |
9 | | - {...otherProps} |
10 | | - /> |
11 | | - ); |
| 5 | + const svgId = Math.random() |
| 6 | + .toString(36) |
| 7 | + .slice(2, 12); |
| 8 | + const processedSVG = useMemo(() => { |
| 9 | + const prefix = `svg-${svgId}`; |
| 10 | + |
| 11 | + return src.replace( |
| 12 | + /<style>([\s\S]*?)<\/style>|class="([^"]+)"|id="([^"]+)"/g, |
| 13 | + (match, styles, classAttr, idAttr) => { |
| 14 | + if (styles) { |
| 15 | + return `<style>${styles |
| 16 | + // Class selectors: only match when followed by { or , (selector context) |
| 17 | + .replace(/(^|[^\w-])\.([a-zA-Z_][\w-]*)(?=\s*[{,])/gm, `$1.${prefix}-$2`) |
| 18 | + // ID selectors: only match when followed by { or , (selector context) |
| 19 | + .replace(/(^|[^\w-])#([a-zA-Z_][\w-]*)(?=\s*[{,])/gm, `$1#${prefix}-$2`)}</style>`; |
| 20 | + } |
| 21 | + if (classAttr) { |
| 22 | + return `class="${classAttr |
| 23 | + .split(/\s+/) |
| 24 | + .map(c => `${prefix}-${c}`) |
| 25 | + .join(' ')}"`; |
| 26 | + } |
| 27 | + if (idAttr) { |
| 28 | + return `id="${prefix}-${idAttr}"`; |
| 29 | + } |
| 30 | + return match; |
| 31 | + }, |
| 32 | + ); |
| 33 | + }, [svgId, src]); |
| 34 | + |
| 35 | + return <div dangerouslySetInnerHTML={{ __html: processedSVG }} {...otherProps} />; |
12 | 36 | }; |
13 | 37 |
|
14 | 38 | InlineSVG.propTypes = { |
|
0 commit comments