react-inlinesvg 2.2.1 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -19
- package/esm/helpers.d.ts +6 -1
- package/esm/helpers.js +28 -8
- package/esm/helpers.js.map +1 -1
- package/esm/index.d.ts +9 -7
- package/esm/index.js +79 -95
- package/esm/index.js.map +1 -1
- package/esm/types.d.ts +2 -3
- package/lib/helpers.d.ts +6 -1
- package/lib/helpers.js +31 -10
- package/lib/helpers.js.map +1 -1
- package/lib/index.d.ts +9 -7
- package/lib/index.js +96 -107
- package/lib/index.js.map +1 -1
- package/lib/types.d.ts +2 -3
- package/package.json +57 -57
- package/src/helpers.ts +35 -7
- package/src/index.tsx +152 -160
- package/src/types.ts +2 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-inlinesvg",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "An SVG loader for React",
|
|
5
5
|
"author": "Gil Barbara <gilbarbara@gmail.com>",
|
|
6
6
|
"contributors": [
|
|
@@ -30,57 +30,47 @@
|
|
|
30
30
|
"src"
|
|
31
31
|
],
|
|
32
32
|
"types": "esm",
|
|
33
|
-
"sideEffects":
|
|
33
|
+
"sideEffects": false,
|
|
34
34
|
"peerDependencies": {
|
|
35
|
-
"react": "^16.8.0 || ^17.0.0"
|
|
35
|
+
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"exenv": "^1.2.2",
|
|
39
|
-
"react-from-dom": "^0.
|
|
39
|
+
"react-from-dom": "^0.6.2"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
+
"@gilbarbara/eslint-config": "^0.2.1",
|
|
43
|
+
"@gilbarbara/helpers": "^0.5.1",
|
|
44
|
+
"@gilbarbara/prettier-config": "^0.1.0",
|
|
42
45
|
"@gilbarbara/tsconfig": "^0.1.0",
|
|
43
|
-
"@size-limit/preset-small-lib": "^
|
|
44
|
-
"@
|
|
45
|
-
"@
|
|
46
|
+
"@size-limit/preset-small-lib": "^7.0.8",
|
|
47
|
+
"@testing-library/jest-dom": "^5.16.4",
|
|
48
|
+
"@testing-library/react": "^13.0.1",
|
|
46
49
|
"@types/exenv": "^1.2.0",
|
|
47
|
-
"@types/fetch-mock": "^7.3.
|
|
48
|
-
"@types/jest": "^
|
|
49
|
-
"@types/node": "^
|
|
50
|
-
"@types/node-fetch": "^2.
|
|
51
|
-
"@types/react": "^
|
|
52
|
-
"@types/react-dom": "^
|
|
53
|
-
"
|
|
54
|
-
"@typescript-eslint/parser": "^4.9.1",
|
|
50
|
+
"@types/fetch-mock": "^7.3.5",
|
|
51
|
+
"@types/jest": "^27.4.1",
|
|
52
|
+
"@types/node": "^17.0.24",
|
|
53
|
+
"@types/node-fetch": "^2.6.1",
|
|
54
|
+
"@types/react": "^18.0.5",
|
|
55
|
+
"@types/react-dom": "^18.0.1",
|
|
56
|
+
"cross-fetch": "^3.1.5",
|
|
55
57
|
"del-cli": "^3.0.1",
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"eslint-config-airbnb": "^18.2.1",
|
|
60
|
-
"eslint-config-prettier": "^7.0.0",
|
|
61
|
-
"eslint-plugin-import": "^2.22.1",
|
|
62
|
-
"eslint-plugin-jsx-a11y": "^6.4.1",
|
|
63
|
-
"eslint-plugin-prettier": "^3.2.0",
|
|
64
|
-
"eslint-plugin-react": "^7.21.5",
|
|
65
|
-
"eslint-plugin-react-hooks": "^4.2.0",
|
|
66
|
-
"http-server": "^0.12.3",
|
|
67
|
-
"husky": "^4.3.5",
|
|
68
|
-
"jest": "^26.6.3",
|
|
58
|
+
"http-server": "^14.1.0",
|
|
59
|
+
"husky": "^7.0.4",
|
|
60
|
+
"jest": "^27.5.1",
|
|
69
61
|
"jest-chain": "^1.1.5",
|
|
70
|
-
"jest-
|
|
71
|
-
"jest-extended": "^0.11.5",
|
|
62
|
+
"jest-extended": "^2.0.0",
|
|
72
63
|
"jest-fetch-mock": "^3.0.3",
|
|
73
|
-
"jest-serializer-html": "^7.
|
|
74
|
-
"jest-watch-typeahead": "^0.
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
"
|
|
79
|
-
"
|
|
80
|
-
"
|
|
81
|
-
"
|
|
82
|
-
"
|
|
83
|
-
"typescript": "^4.1.2"
|
|
64
|
+
"jest-serializer-html": "^7.1.0",
|
|
65
|
+
"jest-watch-typeahead": "^1.0.0",
|
|
66
|
+
"react": "^18.0.0",
|
|
67
|
+
"react-dom": "^18.0.0",
|
|
68
|
+
"repo-tools": "^0.2.2",
|
|
69
|
+
"size-limit": "^7.0.8",
|
|
70
|
+
"start-server-and-test": "^1.14.0",
|
|
71
|
+
"ts-jest": "^27.1.4",
|
|
72
|
+
"ts-node": "^10.7.0",
|
|
73
|
+
"typescript": "^4.6.3"
|
|
84
74
|
},
|
|
85
75
|
"scripts": {
|
|
86
76
|
"build": "npm run clean && npm run build:cjs && npm run build:esm",
|
|
@@ -95,30 +85,40 @@
|
|
|
95
85
|
"test:watch": "jest --watchAll --verbose",
|
|
96
86
|
"lint": "eslint --ext .ts,.tsx src test",
|
|
97
87
|
"format": "prettier \"**/*.{js,jsx,json,yml,yaml,css,less,scss,ts,tsx,md,graphql,mdx}\" --write",
|
|
98
|
-
"validate": "npm run lint && npm run test && npm run build &&
|
|
88
|
+
"validate": "npm run lint && npm run test && npm run build && npm run size",
|
|
99
89
|
"size": "size-limit",
|
|
100
|
-
"prepublishOnly": "npm run validate"
|
|
90
|
+
"prepublishOnly": "npm run validate",
|
|
91
|
+
"prepare": "husky install"
|
|
101
92
|
},
|
|
102
|
-
"
|
|
103
|
-
"
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
"
|
|
93
|
+
"eslintConfig": {
|
|
94
|
+
"extends": [
|
|
95
|
+
"@gilbarbara/eslint-config"
|
|
96
|
+
],
|
|
97
|
+
"overrides": [
|
|
98
|
+
{
|
|
99
|
+
"files": [
|
|
100
|
+
"test/**/*.ts?(x)"
|
|
101
|
+
],
|
|
102
|
+
"rules": {
|
|
103
|
+
"@typescript-eslint/ban-ts-comment": "off",
|
|
104
|
+
"no-console": "off",
|
|
105
|
+
"testing-library/no-container": "off",
|
|
106
|
+
"testing-library/no-node-access": "off"
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
]
|
|
107
110
|
},
|
|
111
|
+
"prettier": "@gilbarbara/prettier-config",
|
|
108
112
|
"size-limit": [
|
|
109
113
|
{
|
|
114
|
+
"name": "lib",
|
|
110
115
|
"path": "./lib/index.js",
|
|
111
|
-
"limit": "
|
|
116
|
+
"limit": "9 kB"
|
|
112
117
|
},
|
|
113
118
|
{
|
|
119
|
+
"name": "esm",
|
|
114
120
|
"path": "./esm/index.js",
|
|
115
|
-
"limit": "
|
|
121
|
+
"limit": "9 kB"
|
|
116
122
|
}
|
|
117
|
-
]
|
|
118
|
-
"husky": {
|
|
119
|
-
"hooks": {
|
|
120
|
-
"post-merge": "repo-tools install-packages",
|
|
121
|
-
"pre-commit": "repo-tools check-remote && npm run validate"
|
|
122
|
-
}
|
|
123
|
-
}
|
|
123
|
+
]
|
|
124
124
|
}
|
package/src/helpers.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { canUseDOM as canUseDOMFlag } from 'exenv';
|
|
2
2
|
|
|
3
|
+
import { PlainObject } from './types';
|
|
4
|
+
|
|
3
5
|
export const STATUS = {
|
|
4
6
|
FAILED: 'failed',
|
|
5
7
|
LOADED: 'loaded',
|
|
@@ -13,6 +15,10 @@ export function canUseDOM(): boolean {
|
|
|
13
15
|
return canUseDOMFlag;
|
|
14
16
|
}
|
|
15
17
|
|
|
18
|
+
export function isSupportedEnvironment(): boolean {
|
|
19
|
+
return supportsInlineSVG() && typeof window !== 'undefined' && window !== null;
|
|
20
|
+
}
|
|
21
|
+
|
|
16
22
|
export function supportsInlineSVG(): boolean {
|
|
17
23
|
/* istanbul ignore next */
|
|
18
24
|
if (!document) {
|
|
@@ -20,12 +26,15 @@ export function supportsInlineSVG(): boolean {
|
|
|
20
26
|
}
|
|
21
27
|
|
|
22
28
|
const div = document.createElement('div');
|
|
29
|
+
|
|
23
30
|
div.innerHTML = '<svg />';
|
|
24
|
-
|
|
31
|
+
const svg = div.firstChild as SVGSVGElement;
|
|
32
|
+
|
|
33
|
+
return !!svg && svg.namespaceURI === 'http://www.w3.org/2000/svg';
|
|
25
34
|
}
|
|
26
35
|
|
|
27
|
-
|
|
28
|
-
return
|
|
36
|
+
function randomCharacter(character: string) {
|
|
37
|
+
return character[Math.floor(Math.random() * character.length)];
|
|
29
38
|
}
|
|
30
39
|
|
|
31
40
|
export function randomString(length: number): string {
|
|
@@ -33,13 +42,32 @@ export function randomString(length: number): string {
|
|
|
33
42
|
const numbers = '1234567890';
|
|
34
43
|
const charset = `${letters}${letters.toUpperCase()}${numbers}`;
|
|
35
44
|
|
|
36
|
-
const randomCharacter = (character: string) =>
|
|
37
|
-
character[Math.floor(Math.random() * character.length)];
|
|
38
|
-
|
|
39
45
|
let R = '';
|
|
40
|
-
|
|
46
|
+
|
|
47
|
+
for (let index = 0; index < length; index++) {
|
|
41
48
|
R += randomCharacter(charset);
|
|
42
49
|
}
|
|
43
50
|
|
|
44
51
|
return R;
|
|
45
52
|
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Remove properties from an object
|
|
56
|
+
*/
|
|
57
|
+
export function omit<T extends PlainObject, K extends keyof T>(
|
|
58
|
+
input: T,
|
|
59
|
+
...filter: K[]
|
|
60
|
+
): Omit<T, K> {
|
|
61
|
+
const output: any = {};
|
|
62
|
+
|
|
63
|
+
for (const key in input) {
|
|
64
|
+
/* istanbul ignore else */
|
|
65
|
+
if ({}.hasOwnProperty.call(input, key)) {
|
|
66
|
+
if (!filter.includes(key as unknown as K)) {
|
|
67
|
+
output[key] = input[key];
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return output as Omit<T, K>;
|
|
73
|
+
}
|
package/src/index.tsx
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
|
|
3
2
|
import convert from 'react-from-dom';
|
|
4
3
|
|
|
5
|
-
import {
|
|
4
|
+
import { canUseDOM, isSupportedEnvironment, omit, randomString, STATUS } from './helpers';
|
|
6
5
|
import { FetchError, Props, State, StorageItem } from './types';
|
|
7
6
|
|
|
8
|
-
const cacheStore: { [key: string]: StorageItem } = Object.create(null);
|
|
7
|
+
export const cacheStore: { [key: string]: StorageItem } = Object.create(null);
|
|
9
8
|
|
|
10
9
|
export default class InlineSVG extends React.PureComponent<Props, State> {
|
|
10
|
+
private isActive = false;
|
|
11
|
+
private readonly hash: string;
|
|
12
|
+
|
|
13
|
+
public static defaultProps = {
|
|
14
|
+
cacheRequests: true,
|
|
15
|
+
uniquifyIDs: false,
|
|
16
|
+
};
|
|
17
|
+
|
|
11
18
|
constructor(props: Props) {
|
|
12
19
|
super(props);
|
|
13
20
|
|
|
@@ -21,14 +28,6 @@ export default class InlineSVG extends React.PureComponent<Props, State> {
|
|
|
21
28
|
this.hash = props.uniqueHash || randomString(8);
|
|
22
29
|
}
|
|
23
30
|
|
|
24
|
-
private isActive = false;
|
|
25
|
-
private readonly hash: string;
|
|
26
|
-
|
|
27
|
-
public static defaultProps = {
|
|
28
|
-
cacheRequests: true,
|
|
29
|
-
uniquifyIDs: false,
|
|
30
|
-
};
|
|
31
|
-
|
|
32
31
|
public componentDidMount(): void {
|
|
33
32
|
this.isActive = true;
|
|
34
33
|
|
|
@@ -54,12 +53,12 @@ export default class InlineSVG extends React.PureComponent<Props, State> {
|
|
|
54
53
|
|
|
55
54
|
this.load();
|
|
56
55
|
}
|
|
57
|
-
} catch (error) {
|
|
56
|
+
} catch (error: any) {
|
|
58
57
|
this.handleError(error);
|
|
59
58
|
}
|
|
60
59
|
}
|
|
61
60
|
|
|
62
|
-
public componentDidUpdate(
|
|
61
|
+
public componentDidUpdate(previousProps: Props, previousState: State): void {
|
|
63
62
|
if (!canUseDOM()) {
|
|
64
63
|
return;
|
|
65
64
|
}
|
|
@@ -67,16 +66,17 @@ export default class InlineSVG extends React.PureComponent<Props, State> {
|
|
|
67
66
|
const { hasCache, status } = this.state;
|
|
68
67
|
const { onLoad, src } = this.props;
|
|
69
68
|
|
|
70
|
-
if (
|
|
69
|
+
if (previousState.status !== STATUS.READY && status === STATUS.READY) {
|
|
71
70
|
/* istanbul ignore else */
|
|
72
71
|
if (onLoad) {
|
|
73
72
|
onLoad(src, hasCache);
|
|
74
73
|
}
|
|
75
74
|
}
|
|
76
75
|
|
|
77
|
-
if (
|
|
76
|
+
if (previousProps.src !== src) {
|
|
78
77
|
if (!src) {
|
|
79
78
|
this.handleError(new Error('Missing src'));
|
|
79
|
+
|
|
80
80
|
return;
|
|
81
81
|
}
|
|
82
82
|
|
|
@@ -88,60 +88,6 @@ export default class InlineSVG extends React.PureComponent<Props, State> {
|
|
|
88
88
|
this.isActive = false;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
private processSVG() {
|
|
92
|
-
const { content } = this.state;
|
|
93
|
-
const { preProcessor } = this.props;
|
|
94
|
-
|
|
95
|
-
if (preProcessor) {
|
|
96
|
-
return preProcessor(content);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return content;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
private updateSVGAttributes(node: SVGSVGElement): SVGSVGElement {
|
|
103
|
-
const { baseURL = '', uniquifyIDs } = this.props;
|
|
104
|
-
const replaceableAttributes = ['id', 'href', 'xlink:href', 'xlink:role', 'xlink:arcrole'];
|
|
105
|
-
const linkAttributes = ['href', 'xlink:href'];
|
|
106
|
-
const isDataValue = (name: string, value: string) =>
|
|
107
|
-
linkAttributes.indexOf(name) >= 0 && (value ? value.indexOf('#') < 0 : false);
|
|
108
|
-
|
|
109
|
-
if (!uniquifyIDs) {
|
|
110
|
-
return node;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
[...node.children].map((d) => {
|
|
114
|
-
if (d.attributes && d.attributes.length) {
|
|
115
|
-
const attributes = Object.values(d.attributes).map((a) => {
|
|
116
|
-
const attr = a;
|
|
117
|
-
const match = a.value.match(/url\((.*?)\)/);
|
|
118
|
-
|
|
119
|
-
if (match && match[1]) {
|
|
120
|
-
attr.value = a.value.replace(match[0], `url(${baseURL}${match[1]}__${this.hash})`);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
return attr;
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
replaceableAttributes.forEach((r) => {
|
|
127
|
-
const attribute = attributes.find((a) => a.name === r);
|
|
128
|
-
|
|
129
|
-
if (attribute && !isDataValue(r, attribute.value)) {
|
|
130
|
-
attribute.value = `${attribute.value}__${this.hash}`;
|
|
131
|
-
}
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
if (d.children.length) {
|
|
136
|
-
return this.updateSVGAttributes(d as SVGSVGElement);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
return d;
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
return node;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
91
|
private getNode() {
|
|
146
92
|
const { description, title } = this.props;
|
|
147
93
|
|
|
@@ -163,6 +109,7 @@ export default class InlineSVG extends React.PureComponent<Props, State> {
|
|
|
163
109
|
}
|
|
164
110
|
|
|
165
111
|
const descElement = document.createElement('desc');
|
|
112
|
+
|
|
166
113
|
descElement.innerHTML = description;
|
|
167
114
|
svg.prepend(descElement);
|
|
168
115
|
}
|
|
@@ -175,12 +122,13 @@ export default class InlineSVG extends React.PureComponent<Props, State> {
|
|
|
175
122
|
}
|
|
176
123
|
|
|
177
124
|
const titleElement = document.createElement('title');
|
|
125
|
+
|
|
178
126
|
titleElement.innerHTML = title;
|
|
179
127
|
svg.prepend(titleElement);
|
|
180
128
|
}
|
|
181
129
|
|
|
182
130
|
return svg;
|
|
183
|
-
} catch (error) {
|
|
131
|
+
} catch (error: any) {
|
|
184
132
|
return this.handleError(error);
|
|
185
133
|
}
|
|
186
134
|
}
|
|
@@ -198,64 +146,11 @@ export default class InlineSVG extends React.PureComponent<Props, State> {
|
|
|
198
146
|
element,
|
|
199
147
|
status: STATUS.READY,
|
|
200
148
|
});
|
|
201
|
-
} catch (error) {
|
|
149
|
+
} catch (error: any) {
|
|
202
150
|
this.handleError(new Error(error.message));
|
|
203
151
|
}
|
|
204
152
|
}
|
|
205
153
|
|
|
206
|
-
private load() {
|
|
207
|
-
/* istanbul ignore else */
|
|
208
|
-
if (this.isActive) {
|
|
209
|
-
this.setState(
|
|
210
|
-
{
|
|
211
|
-
content: '',
|
|
212
|
-
element: null,
|
|
213
|
-
status: STATUS.LOADING,
|
|
214
|
-
},
|
|
215
|
-
() => {
|
|
216
|
-
const { cacheRequests, src } = this.props;
|
|
217
|
-
const cache = cacheRequests && cacheStore[src];
|
|
218
|
-
|
|
219
|
-
if (cache) {
|
|
220
|
-
/* istanbul ignore else */
|
|
221
|
-
if (cache.status === STATUS.LOADING) {
|
|
222
|
-
cache.queue.push(this.handleCacheQueue);
|
|
223
|
-
} else if (cache.status === STATUS.LOADED) {
|
|
224
|
-
this.handleLoad(cache.content);
|
|
225
|
-
}
|
|
226
|
-
return;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
const dataURI = src.match(/data:image\/svg[^,]*?(;base64)?,(.*)/);
|
|
230
|
-
let inlineSrc;
|
|
231
|
-
|
|
232
|
-
if (dataURI) {
|
|
233
|
-
inlineSrc = dataURI[1] ? atob(dataURI[2]) : decodeURIComponent(dataURI[2]);
|
|
234
|
-
} else if (src.indexOf('<svg') >= 0) {
|
|
235
|
-
inlineSrc = src;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
if (inlineSrc) {
|
|
239
|
-
this.handleLoad(inlineSrc);
|
|
240
|
-
return;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
this.request();
|
|
244
|
-
},
|
|
245
|
-
);
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
private handleCacheQueue = (content: string | Error) => {
|
|
250
|
-
/* istanbul ignore else */
|
|
251
|
-
if (typeof content === 'string') {
|
|
252
|
-
this.handleLoad(content);
|
|
253
|
-
return;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
this.handleError(content);
|
|
257
|
-
};
|
|
258
|
-
|
|
259
154
|
private handleLoad = (content: string) => {
|
|
260
155
|
/* istanbul ignore else */
|
|
261
156
|
if (this.isActive) {
|
|
@@ -286,15 +181,15 @@ export default class InlineSVG extends React.PureComponent<Props, State> {
|
|
|
286
181
|
};
|
|
287
182
|
|
|
288
183
|
private request = () => {
|
|
289
|
-
const { cacheRequests, src } = this.props;
|
|
184
|
+
const { cacheRequests, fetchOptions, src } = this.props;
|
|
290
185
|
|
|
291
186
|
try {
|
|
292
187
|
if (cacheRequests) {
|
|
293
|
-
cacheStore[src] = { content: '', status: STATUS.LOADING
|
|
188
|
+
cacheStore[src] = { content: '', status: STATUS.LOADING };
|
|
294
189
|
}
|
|
295
190
|
|
|
296
|
-
return fetch(src)
|
|
297
|
-
.then(
|
|
191
|
+
return fetch(src, fetchOptions)
|
|
192
|
+
.then(response => {
|
|
298
193
|
const contentType = response.headers.get('content-type');
|
|
299
194
|
const [fileType] = (contentType || '').split(/ ?; ?/);
|
|
300
195
|
|
|
@@ -302,13 +197,24 @@ export default class InlineSVG extends React.PureComponent<Props, State> {
|
|
|
302
197
|
throw new Error('Not found');
|
|
303
198
|
}
|
|
304
199
|
|
|
305
|
-
if (!['image/svg+xml', 'text/plain'].some(
|
|
200
|
+
if (!['image/svg+xml', 'text/plain'].some(d => fileType.includes(d))) {
|
|
306
201
|
throw new Error(`Content type isn't valid: ${fileType}`);
|
|
307
202
|
}
|
|
308
203
|
|
|
309
204
|
return response.text();
|
|
310
205
|
})
|
|
311
|
-
.then(
|
|
206
|
+
.then(content => {
|
|
207
|
+
const { src: currentSrc } = this.props;
|
|
208
|
+
|
|
209
|
+
// the current src don't match the previous one, skipping...
|
|
210
|
+
if (src !== currentSrc) {
|
|
211
|
+
if (cacheStore[src].status === STATUS.LOADING) {
|
|
212
|
+
delete cacheStore[src];
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
|
|
312
218
|
this.handleLoad(content);
|
|
313
219
|
|
|
314
220
|
/* istanbul ignore else */
|
|
@@ -319,16 +225,10 @@ export default class InlineSVG extends React.PureComponent<Props, State> {
|
|
|
319
225
|
if (cache) {
|
|
320
226
|
cache.content = content;
|
|
321
227
|
cache.status = STATUS.LOADED;
|
|
322
|
-
|
|
323
|
-
cache.queue = cache.queue.filter((cb) => {
|
|
324
|
-
cb(content);
|
|
325
|
-
|
|
326
|
-
return false;
|
|
327
|
-
});
|
|
328
228
|
}
|
|
329
229
|
}
|
|
330
230
|
})
|
|
331
|
-
.catch(
|
|
231
|
+
.catch(error => {
|
|
332
232
|
this.handleError(error);
|
|
333
233
|
|
|
334
234
|
/* istanbul ignore else */
|
|
@@ -337,47 +237,139 @@ export default class InlineSVG extends React.PureComponent<Props, State> {
|
|
|
337
237
|
|
|
338
238
|
/* istanbul ignore else */
|
|
339
239
|
if (cache) {
|
|
340
|
-
cache.queue.forEach((cb: (content: string) => void) => {
|
|
341
|
-
cb(error);
|
|
342
|
-
});
|
|
343
|
-
|
|
344
240
|
delete cacheStore[src];
|
|
345
241
|
}
|
|
346
242
|
}
|
|
347
243
|
});
|
|
348
|
-
} catch (error) {
|
|
244
|
+
} catch (error: any) {
|
|
349
245
|
return this.handleError(new Error(error.message));
|
|
350
246
|
}
|
|
351
247
|
};
|
|
352
248
|
|
|
249
|
+
private load() {
|
|
250
|
+
/* istanbul ignore else */
|
|
251
|
+
if (this.isActive) {
|
|
252
|
+
this.setState(
|
|
253
|
+
{
|
|
254
|
+
content: '',
|
|
255
|
+
element: null,
|
|
256
|
+
status: STATUS.LOADING,
|
|
257
|
+
},
|
|
258
|
+
() => {
|
|
259
|
+
const { cacheRequests, src } = this.props;
|
|
260
|
+
const cache = cacheRequests && cacheStore[src];
|
|
261
|
+
|
|
262
|
+
if (cache && cache.status === STATUS.LOADED) {
|
|
263
|
+
this.handleLoad(cache.content);
|
|
264
|
+
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const dataURI = src.match(/data:image\/svg[^,]*?(;base64)?,(.*)/);
|
|
269
|
+
let inlineSrc;
|
|
270
|
+
|
|
271
|
+
if (dataURI) {
|
|
272
|
+
inlineSrc = dataURI[1] ? window.atob(dataURI[2]) : decodeURIComponent(dataURI[2]);
|
|
273
|
+
} else if (src.includes('<svg')) {
|
|
274
|
+
inlineSrc = src;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (inlineSrc) {
|
|
278
|
+
this.handleLoad(inlineSrc);
|
|
279
|
+
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
this.request();
|
|
284
|
+
},
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
private updateSVGAttributes(node: SVGSVGElement): SVGSVGElement {
|
|
290
|
+
const { baseURL = '', uniquifyIDs } = this.props;
|
|
291
|
+
const replaceableAttributes = ['id', 'href', 'xlink:href', 'xlink:role', 'xlink:arcrole'];
|
|
292
|
+
const linkAttributes = ['href', 'xlink:href'];
|
|
293
|
+
const isDataValue = (name: string, value: string) =>
|
|
294
|
+
linkAttributes.includes(name) && (value ? !value.includes('#') : false);
|
|
295
|
+
|
|
296
|
+
if (!uniquifyIDs) {
|
|
297
|
+
return node;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
[...node.children].map(d => {
|
|
301
|
+
if (d.attributes && d.attributes.length) {
|
|
302
|
+
const attributes = Object.values(d.attributes).map(a => {
|
|
303
|
+
const attribute = a;
|
|
304
|
+
const match = a.value.match(/url\((.*?)\)/);
|
|
305
|
+
|
|
306
|
+
if (match && match[1]) {
|
|
307
|
+
attribute.value = a.value.replace(match[0], `url(${baseURL}${match[1]}__${this.hash})`);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return attribute;
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
replaceableAttributes.forEach(r => {
|
|
314
|
+
const attribute = attributes.find(a => a.name === r);
|
|
315
|
+
|
|
316
|
+
if (attribute && !isDataValue(r, attribute.value)) {
|
|
317
|
+
attribute.value = `${attribute.value}__${this.hash}`;
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (d.children.length) {
|
|
323
|
+
return this.updateSVGAttributes(d as SVGSVGElement);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
return d;
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
return node;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
private processSVG() {
|
|
333
|
+
const { content } = this.state;
|
|
334
|
+
const { preProcessor } = this.props;
|
|
335
|
+
|
|
336
|
+
if (preProcessor) {
|
|
337
|
+
return preProcessor(content);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
return content;
|
|
341
|
+
}
|
|
342
|
+
|
|
353
343
|
public render(): React.ReactNode {
|
|
354
344
|
const { element, status } = this.state;
|
|
355
|
-
const {
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
345
|
+
const { children = null, innerRef, loader = null } = this.props;
|
|
346
|
+
const elementProps = omit(
|
|
347
|
+
this.props,
|
|
348
|
+
'baseURL',
|
|
349
|
+
'cacheRequests',
|
|
350
|
+
'children',
|
|
351
|
+
'description',
|
|
352
|
+
'fetchOptions',
|
|
353
|
+
'innerRef',
|
|
354
|
+
'loader',
|
|
355
|
+
'onError',
|
|
356
|
+
'onLoad',
|
|
357
|
+
'preProcessor',
|
|
358
|
+
'src',
|
|
359
|
+
'title',
|
|
360
|
+
'uniqueHash',
|
|
361
|
+
'uniquifyIDs',
|
|
362
|
+
);
|
|
371
363
|
|
|
372
364
|
if (!canUseDOM()) {
|
|
373
365
|
return loader;
|
|
374
366
|
}
|
|
375
367
|
|
|
376
368
|
if (element) {
|
|
377
|
-
return React.cloneElement(element as React.ReactElement, { ref: innerRef, ...
|
|
369
|
+
return React.cloneElement(element as React.ReactElement, { ref: innerRef, ...elementProps });
|
|
378
370
|
}
|
|
379
371
|
|
|
380
|
-
if ([STATUS.UNSUPPORTED, STATUS.FAILED].
|
|
372
|
+
if ([STATUS.UNSUPPORTED, STATUS.FAILED].includes(status)) {
|
|
381
373
|
return children;
|
|
382
374
|
}
|
|
383
375
|
|