react-inlinesvg 3.0.1 → 3.0.3
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 +14 -15
- package/esm/helpers.d.ts +7 -7
- package/esm/helpers.js +3 -3
- package/esm/helpers.js.map +1 -1
- package/esm/index.d.ts +6 -6
- package/esm/index.js +159 -111
- package/esm/index.js.map +1 -1
- package/esm/types.d.ts +9 -7
- package/lib/helpers.d.ts +7 -7
- package/lib/helpers.js +3 -3
- package/lib/helpers.js.map +1 -1
- package/lib/index.d.ts +6 -6
- package/lib/index.js +159 -111
- package/lib/index.js.map +1 -1
- package/lib/types.d.ts +9 -7
- package/package.json +23 -23
- package/src/helpers.ts +5 -5
- package/src/index.tsx +116 -119
- package/src/types.ts +8 -4
package/src/index.tsx
CHANGED
|
@@ -2,14 +2,14 @@ import * as React from 'react';
|
|
|
2
2
|
import convert from 'react-from-dom';
|
|
3
3
|
|
|
4
4
|
import { canUseDOM, isSupportedEnvironment, omit, randomString, STATUS } from './helpers';
|
|
5
|
-
import { FetchError, Props, State, StorageItem } from './types';
|
|
5
|
+
import { FetchError, Props, State, Status, StorageItem } from './types';
|
|
6
6
|
|
|
7
7
|
export const cacheStore: { [key: string]: StorageItem } = Object.create(null);
|
|
8
8
|
|
|
9
9
|
export default class InlineSVG extends React.PureComponent<Props, State> {
|
|
10
|
-
private isInitialized = false;
|
|
11
|
-
private isActive = false;
|
|
12
10
|
private readonly hash: string;
|
|
11
|
+
private isActive = false;
|
|
12
|
+
private isInitialized = false;
|
|
13
13
|
|
|
14
14
|
public static defaultProps = {
|
|
15
15
|
cacheRequests: true,
|
|
@@ -23,7 +23,7 @@ export default class InlineSVG extends React.PureComponent<Props, State> {
|
|
|
23
23
|
content: '',
|
|
24
24
|
element: null,
|
|
25
25
|
hasCache: !!props.cacheRequests && !!cacheStore[props.src],
|
|
26
|
-
status: STATUS.
|
|
26
|
+
status: STATUS.IDLE,
|
|
27
27
|
};
|
|
28
28
|
|
|
29
29
|
this.hash = props.uniqueHash || randomString(8);
|
|
@@ -41,7 +41,7 @@ export default class InlineSVG extends React.PureComponent<Props, State> {
|
|
|
41
41
|
|
|
42
42
|
try {
|
|
43
43
|
/* istanbul ignore else */
|
|
44
|
-
if (status === STATUS.
|
|
44
|
+
if (status === STATUS.IDLE) {
|
|
45
45
|
/* istanbul ignore else */
|
|
46
46
|
if (!isSupportedEnvironment()) {
|
|
47
47
|
throw new Error('Browser does not support SVG');
|
|
@@ -91,6 +91,24 @@ export default class InlineSVG extends React.PureComponent<Props, State> {
|
|
|
91
91
|
this.isActive = false;
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
private getElement() {
|
|
95
|
+
try {
|
|
96
|
+
const node = this.getNode() as Node;
|
|
97
|
+
const element = convert(node);
|
|
98
|
+
|
|
99
|
+
if (!element || !React.isValidElement(element)) {
|
|
100
|
+
throw new Error('Could not convert the src to a React element');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
this.setState({
|
|
104
|
+
element,
|
|
105
|
+
status: STATUS.READY,
|
|
106
|
+
});
|
|
107
|
+
} catch (error: any) {
|
|
108
|
+
this.handleError(new Error(error.message));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
94
112
|
private getNode() {
|
|
95
113
|
const { description, title } = this.props;
|
|
96
114
|
|
|
@@ -111,23 +129,25 @@ export default class InlineSVG extends React.PureComponent<Props, State> {
|
|
|
111
129
|
originalDesc.parentNode.removeChild(originalDesc);
|
|
112
130
|
}
|
|
113
131
|
|
|
114
|
-
const descElement = document.
|
|
132
|
+
const descElement = document.createElementNS('http://www.w3.org/2000/svg', 'desc');
|
|
115
133
|
|
|
116
134
|
descElement.innerHTML = description;
|
|
117
135
|
svg.prepend(descElement);
|
|
118
136
|
}
|
|
119
137
|
|
|
120
|
-
if (title) {
|
|
138
|
+
if (typeof title !== 'undefined') {
|
|
121
139
|
const originalTitle = svg.querySelector('title');
|
|
122
140
|
|
|
123
141
|
if (originalTitle && originalTitle.parentNode) {
|
|
124
142
|
originalTitle.parentNode.removeChild(originalTitle);
|
|
125
143
|
}
|
|
126
144
|
|
|
127
|
-
|
|
145
|
+
if (title) {
|
|
146
|
+
const titleElement = document.createElementNS('http://www.w3.org/2000/svg', 'title');
|
|
128
147
|
|
|
129
|
-
|
|
130
|
-
|
|
148
|
+
titleElement.innerHTML = title;
|
|
149
|
+
svg.prepend(titleElement);
|
|
150
|
+
}
|
|
131
151
|
}
|
|
132
152
|
|
|
133
153
|
return svg;
|
|
@@ -136,37 +156,6 @@ export default class InlineSVG extends React.PureComponent<Props, State> {
|
|
|
136
156
|
}
|
|
137
157
|
}
|
|
138
158
|
|
|
139
|
-
private getElement() {
|
|
140
|
-
try {
|
|
141
|
-
const node = this.getNode() as Node;
|
|
142
|
-
const element = convert(node);
|
|
143
|
-
|
|
144
|
-
if (!element || !React.isValidElement(element)) {
|
|
145
|
-
throw new Error('Could not convert the src to a React element');
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
this.setState({
|
|
149
|
-
element,
|
|
150
|
-
status: STATUS.READY,
|
|
151
|
-
});
|
|
152
|
-
} catch (error: any) {
|
|
153
|
-
this.handleError(new Error(error.message));
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
private handleLoad = (content: string) => {
|
|
158
|
-
/* istanbul ignore else */
|
|
159
|
-
if (this.isActive) {
|
|
160
|
-
this.setState(
|
|
161
|
-
{
|
|
162
|
-
content,
|
|
163
|
-
status: STATUS.LOADED,
|
|
164
|
-
},
|
|
165
|
-
this.getElement,
|
|
166
|
-
);
|
|
167
|
-
}
|
|
168
|
-
};
|
|
169
|
-
|
|
170
159
|
private handleError = (error: Error | FetchError) => {
|
|
171
160
|
const { onError } = this.props;
|
|
172
161
|
const status =
|
|
@@ -183,69 +172,17 @@ export default class InlineSVG extends React.PureComponent<Props, State> {
|
|
|
183
172
|
}
|
|
184
173
|
};
|
|
185
174
|
|
|
186
|
-
private
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
.
|
|
196
|
-
|
|
197
|
-
const [fileType] = (contentType || '').split(/ ?; ?/);
|
|
198
|
-
|
|
199
|
-
if (response.status > 299) {
|
|
200
|
-
throw new Error('Not found');
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
if (!['image/svg+xml', 'text/plain'].some(d => fileType.includes(d))) {
|
|
204
|
-
throw new Error(`Content type isn't valid: ${fileType}`);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
return response.text();
|
|
208
|
-
})
|
|
209
|
-
.then(content => {
|
|
210
|
-
const { src: currentSrc } = this.props;
|
|
211
|
-
|
|
212
|
-
// the current src don't match the previous one, skipping...
|
|
213
|
-
if (src !== currentSrc) {
|
|
214
|
-
if (cacheStore[src].status === STATUS.LOADING) {
|
|
215
|
-
delete cacheStore[src];
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
return;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
this.handleLoad(content);
|
|
222
|
-
|
|
223
|
-
/* istanbul ignore else */
|
|
224
|
-
if (cacheRequests) {
|
|
225
|
-
const cache = cacheStore[src];
|
|
226
|
-
|
|
227
|
-
/* istanbul ignore else */
|
|
228
|
-
if (cache) {
|
|
229
|
-
cache.content = content;
|
|
230
|
-
cache.status = STATUS.LOADED;
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
})
|
|
234
|
-
.catch(error => {
|
|
235
|
-
this.handleError(error);
|
|
236
|
-
|
|
237
|
-
/* istanbul ignore else */
|
|
238
|
-
if (cacheRequests) {
|
|
239
|
-
const cache = cacheStore[src];
|
|
240
|
-
|
|
241
|
-
/* istanbul ignore else */
|
|
242
|
-
if (cache) {
|
|
243
|
-
delete cacheStore[src];
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
});
|
|
247
|
-
} catch (error: any) {
|
|
248
|
-
return this.handleError(new Error(error.message));
|
|
175
|
+
private handleLoad = (content: string, hasCache = false) => {
|
|
176
|
+
/* istanbul ignore else */
|
|
177
|
+
if (this.isActive) {
|
|
178
|
+
this.setState(
|
|
179
|
+
{
|
|
180
|
+
content,
|
|
181
|
+
hasCache,
|
|
182
|
+
status: STATUS.LOADED,
|
|
183
|
+
},
|
|
184
|
+
this.getElement,
|
|
185
|
+
);
|
|
249
186
|
}
|
|
250
187
|
};
|
|
251
188
|
|
|
@@ -256,6 +193,7 @@ export default class InlineSVG extends React.PureComponent<Props, State> {
|
|
|
256
193
|
{
|
|
257
194
|
content: '',
|
|
258
195
|
element: null,
|
|
196
|
+
hasCache: false,
|
|
259
197
|
status: STATUS.LOADING,
|
|
260
198
|
},
|
|
261
199
|
() => {
|
|
@@ -263,12 +201,12 @@ export default class InlineSVG extends React.PureComponent<Props, State> {
|
|
|
263
201
|
const cache = cacheRequests && cacheStore[src];
|
|
264
202
|
|
|
265
203
|
if (cache && cache.status === STATUS.LOADED) {
|
|
266
|
-
this.handleLoad(cache.content);
|
|
204
|
+
this.handleLoad(cache.content, true);
|
|
267
205
|
|
|
268
206
|
return;
|
|
269
207
|
}
|
|
270
208
|
|
|
271
|
-
const dataURI = src.match(
|
|
209
|
+
const dataURI = src.match(/^data:image\/svg[^,]*?(;base64)?,(.*)/u);
|
|
272
210
|
let inlineSrc;
|
|
273
211
|
|
|
274
212
|
if (dataURI) {
|
|
@@ -289,6 +227,76 @@ export default class InlineSVG extends React.PureComponent<Props, State> {
|
|
|
289
227
|
}
|
|
290
228
|
}
|
|
291
229
|
|
|
230
|
+
private processSVG() {
|
|
231
|
+
const { content } = this.state;
|
|
232
|
+
const { preProcessor } = this.props;
|
|
233
|
+
|
|
234
|
+
if (preProcessor) {
|
|
235
|
+
return preProcessor(content);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return content;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
private request = async () => {
|
|
242
|
+
const { cacheRequests, fetchOptions, src } = this.props;
|
|
243
|
+
|
|
244
|
+
if (cacheRequests) {
|
|
245
|
+
cacheStore[src] = { content: '', status: STATUS.LOADING };
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
try {
|
|
249
|
+
const response = await fetch(src, fetchOptions);
|
|
250
|
+
const contentType = response.headers.get('content-type');
|
|
251
|
+
const [fileType] = (contentType || '').split(/ ?; ?/);
|
|
252
|
+
|
|
253
|
+
if (response.status > 299) {
|
|
254
|
+
throw new Error('Not found');
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (!['image/svg+xml', 'text/plain'].some(d => fileType.includes(d))) {
|
|
258
|
+
throw new Error(`Content type isn't valid: ${fileType}`);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const content: string = await response.text();
|
|
262
|
+
const { src: currentSrc } = this.props;
|
|
263
|
+
|
|
264
|
+
// the current src don't match the previous one, skipping...
|
|
265
|
+
if (src !== currentSrc) {
|
|
266
|
+
if (cacheStore[src].status === STATUS.LOADING) {
|
|
267
|
+
delete cacheStore[src];
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
this.handleLoad(content);
|
|
274
|
+
|
|
275
|
+
/* istanbul ignore else */
|
|
276
|
+
if (cacheRequests) {
|
|
277
|
+
const cache = cacheStore[src];
|
|
278
|
+
|
|
279
|
+
/* istanbul ignore else */
|
|
280
|
+
if (cache) {
|
|
281
|
+
cache.content = content;
|
|
282
|
+
cache.status = STATUS.LOADED;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
} catch (error: any) {
|
|
286
|
+
this.handleError(error);
|
|
287
|
+
|
|
288
|
+
/* istanbul ignore else */
|
|
289
|
+
if (cacheRequests) {
|
|
290
|
+
const cache = cacheStore[src];
|
|
291
|
+
|
|
292
|
+
/* istanbul ignore else */
|
|
293
|
+
if (cache) {
|
|
294
|
+
delete cacheStore[src];
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
|
|
292
300
|
private updateSVGAttributes(node: SVGSVGElement): SVGSVGElement {
|
|
293
301
|
const { baseURL = '', uniquifyIDs } = this.props;
|
|
294
302
|
const replaceableAttributes = ['id', 'href', 'xlink:href', 'xlink:role', 'xlink:arcrole'];
|
|
@@ -300,7 +308,7 @@ export default class InlineSVG extends React.PureComponent<Props, State> {
|
|
|
300
308
|
return node;
|
|
301
309
|
}
|
|
302
310
|
|
|
303
|
-
[...node.children].
|
|
311
|
+
[...node.children].forEach(d => {
|
|
304
312
|
if (d.attributes && d.attributes.length) {
|
|
305
313
|
const attributes = Object.values(d.attributes).map(a => {
|
|
306
314
|
const attribute = a;
|
|
@@ -332,17 +340,6 @@ export default class InlineSVG extends React.PureComponent<Props, State> {
|
|
|
332
340
|
return node;
|
|
333
341
|
}
|
|
334
342
|
|
|
335
|
-
private processSVG() {
|
|
336
|
-
const { content } = this.state;
|
|
337
|
-
const { preProcessor } = this.props;
|
|
338
|
-
|
|
339
|
-
if (preProcessor) {
|
|
340
|
-
return preProcessor(content);
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
return content;
|
|
344
|
-
}
|
|
345
|
-
|
|
346
343
|
public render(): React.ReactNode {
|
|
347
344
|
const { element, status } = this.state;
|
|
348
345
|
const { children = null, innerRef, loader = null } = this.props;
|
|
@@ -372,7 +369,7 @@ export default class InlineSVG extends React.PureComponent<Props, State> {
|
|
|
372
369
|
return React.cloneElement(element as React.ReactElement, { ref: innerRef, ...elementProps });
|
|
373
370
|
}
|
|
374
371
|
|
|
375
|
-
if ([STATUS.UNSUPPORTED, STATUS.FAILED].includes(status)) {
|
|
372
|
+
if (([STATUS.UNSUPPORTED, STATUS.FAILED] as Status[]).includes(status)) {
|
|
376
373
|
return children;
|
|
377
374
|
}
|
|
378
375
|
|
package/src/types.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
|
|
3
|
+
import { STATUS } from './helpers';
|
|
4
|
+
|
|
3
5
|
export type ErrorCallback = (error: Error | FetchError) => void;
|
|
4
6
|
export type LoadCallback = (src: string, isCached: boolean) => void;
|
|
5
|
-
export type PlainObject<T = unknown> = Record<string
|
|
7
|
+
export type PlainObject<T = unknown> = Record<string, T>;
|
|
6
8
|
export type PreProcessorCallback = (code: string) => string;
|
|
7
9
|
|
|
8
10
|
export interface Props extends Omit<React.SVGProps<SVGElement>, 'onLoad' | 'onError' | 'ref'> {
|
|
@@ -17,7 +19,7 @@ export interface Props extends Omit<React.SVGProps<SVGElement>, 'onLoad' | 'onEr
|
|
|
17
19
|
onLoad?: LoadCallback;
|
|
18
20
|
preProcessor?: PreProcessorCallback;
|
|
19
21
|
src: string;
|
|
20
|
-
title?: string;
|
|
22
|
+
title?: string | null;
|
|
21
23
|
uniqueHash?: string;
|
|
22
24
|
uniquifyIDs?: boolean;
|
|
23
25
|
}
|
|
@@ -26,7 +28,7 @@ export interface State {
|
|
|
26
28
|
content: string;
|
|
27
29
|
element: React.ReactNode;
|
|
28
30
|
hasCache: boolean;
|
|
29
|
-
status:
|
|
31
|
+
status: Status;
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
export interface FetchError extends Error {
|
|
@@ -36,7 +38,9 @@ export interface FetchError extends Error {
|
|
|
36
38
|
type: string;
|
|
37
39
|
}
|
|
38
40
|
|
|
41
|
+
export type Status = (typeof STATUS)[keyof typeof STATUS];
|
|
42
|
+
|
|
39
43
|
export interface StorageItem {
|
|
40
44
|
content: string;
|
|
41
|
-
status:
|
|
45
|
+
status: Status;
|
|
42
46
|
}
|