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/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.PENDING,
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.PENDING) {
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.createElement('desc');
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
- const titleElement = document.createElement('title');
145
+ if (title) {
146
+ const titleElement = document.createElementNS('http://www.w3.org/2000/svg', 'title');
128
147
 
129
- titleElement.innerHTML = title;
130
- svg.prepend(titleElement);
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 request = () => {
187
- const { cacheRequests, fetchOptions, src } = this.props;
188
-
189
- try {
190
- if (cacheRequests) {
191
- cacheStore[src] = { content: '', status: STATUS.LOADING };
192
- }
193
-
194
- return fetch(src, fetchOptions)
195
- .then(response => {
196
- const contentType = response.headers.get('content-type');
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(/data:image\/svg[^,]*?(;base64)?,(.*)/);
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].map(d => {
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 | number | symbol, T>;
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: string;
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: string;
45
+ status: Status;
42
46
  }