react-inlinesvg 4.0.6 → 4.1.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/dist/index.d.mts CHANGED
@@ -14,7 +14,7 @@ type ErrorCallback = (error: Error | FetchError) => void;
14
14
  type LoadCallback = (src: string, isCached: boolean) => void;
15
15
  type PlainObject<T = unknown> = Record<string, T>;
16
16
  type PreProcessorCallback = (code: string) => string;
17
- interface Props extends Omit<React.SVGProps<SVGElement>, 'onLoad' | 'onError' | 'ref'> {
17
+ type Props = Simplify<Omit<React.SVGProps<SVGElement>, 'onLoad' | 'onError' | 'ref'> & {
18
18
  baseURL?: string;
19
19
  cacheRequests?: boolean;
20
20
  children?: React.ReactNode;
@@ -29,7 +29,7 @@ interface Props extends Omit<React.SVGProps<SVGElement>, 'onLoad' | 'onError' |
29
29
  title?: string | null;
30
30
  uniqueHash?: string;
31
31
  uniquifyIDs?: boolean;
32
- }
32
+ }>;
33
33
  interface State {
34
34
  content: string;
35
35
  element: React.ReactNode;
@@ -42,6 +42,9 @@ interface FetchError extends Error {
42
42
  message: string;
43
43
  type: string;
44
44
  }
45
+ type Simplify<T> = {
46
+ [KeyType in keyof T]: T[KeyType];
47
+ } & {};
45
48
  type Status = (typeof STATUS)[keyof typeof STATUS];
46
49
  interface StorageItem {
47
50
  content: string;
@@ -70,4 +73,4 @@ declare class CacheStore {
70
73
  declare let cacheStore: CacheStore;
71
74
  declare function InlineSVG(props: Props): string | number | boolean | Iterable<React.ReactNode> | react_jsx_runtime.JSX.Element | null | undefined;
72
75
 
73
- export { ErrorCallback, FetchError, LoadCallback, PlainObject, PreProcessorCallback, Props, State, Status, StorageItem, cacheStore, InlineSVG as default };
76
+ export { ErrorCallback, FetchError, LoadCallback, PlainObject, PreProcessorCallback, Props, Simplify, State, Status, StorageItem, cacheStore, InlineSVG as default };
package/dist/index.d.ts CHANGED
@@ -14,7 +14,7 @@ type ErrorCallback = (error: Error | FetchError) => void;
14
14
  type LoadCallback = (src: string, isCached: boolean) => void;
15
15
  type PlainObject<T = unknown> = Record<string, T>;
16
16
  type PreProcessorCallback = (code: string) => string;
17
- interface Props extends Omit<React.SVGProps<SVGElement>, 'onLoad' | 'onError' | 'ref'> {
17
+ type Props = Simplify<Omit<React.SVGProps<SVGElement>, 'onLoad' | 'onError' | 'ref'> & {
18
18
  baseURL?: string;
19
19
  cacheRequests?: boolean;
20
20
  children?: React.ReactNode;
@@ -29,7 +29,7 @@ interface Props extends Omit<React.SVGProps<SVGElement>, 'onLoad' | 'onError' |
29
29
  title?: string | null;
30
30
  uniqueHash?: string;
31
31
  uniquifyIDs?: boolean;
32
- }
32
+ }>;
33
33
  interface State {
34
34
  content: string;
35
35
  element: React.ReactNode;
@@ -42,6 +42,9 @@ interface FetchError extends Error {
42
42
  message: string;
43
43
  type: string;
44
44
  }
45
+ type Simplify<T> = {
46
+ [KeyType in keyof T]: T[KeyType];
47
+ } & {};
45
48
  type Status = (typeof STATUS)[keyof typeof STATUS];
46
49
  interface StorageItem {
47
50
  content: string;
@@ -70,5 +73,5 @@ declare class CacheStore {
70
73
  declare let cacheStore: CacheStore;
71
74
  declare function InlineSVG(props: Props): string | number | boolean | Iterable<React.ReactNode> | react_jsx_runtime.JSX.Element | null | undefined;
72
75
 
73
- export { ErrorCallback, FetchError, LoadCallback, PlainObject, PreProcessorCallback, Props, State, Status, StorageItem, cacheStore, InlineSVG as default };
76
+ export { ErrorCallback, FetchError, LoadCallback, PlainObject, PreProcessorCallback, Props, Simplify, State, Status, StorageItem, cacheStore, InlineSVG as default };
74
77
  export = InlineSVG
package/dist/index.js CHANGED
@@ -39,7 +39,7 @@ __export(src_exports, {
39
39
  default: () => InlineSVG
40
40
  });
41
41
  module.exports = __toCommonJS(src_exports);
42
- var React = __toESM(require("react"));
42
+ var import_react2 = require("react");
43
43
  var import_react_from_dom = __toESM(require("react-from-dom"));
44
44
 
45
45
  // src/config.ts
@@ -238,267 +238,310 @@ var CacheStore = class {
238
238
  }
239
239
  };
240
240
 
241
+ // src/hooks.tsx
242
+ var import_react = require("react");
243
+ function usePrevious(state) {
244
+ const ref = (0, import_react.useRef)();
245
+ (0, import_react.useEffect)(() => {
246
+ ref.current = state;
247
+ });
248
+ return ref.current;
249
+ }
250
+
241
251
  // src/index.tsx
242
252
  var import_jsx_runtime = require("react/jsx-runtime");
243
253
  var cacheStore;
244
- var ReactInlineSVG = class extends React.PureComponent {
245
- constructor(props) {
246
- super(props);
247
- __publicField(this, "hash");
248
- __publicField(this, "isActive", false);
249
- __publicField(this, "isInitialized", false);
250
- __publicField(this, "fetchContent", async () => {
251
- const { fetchOptions, src } = this.props;
252
- const content = await request(src, fetchOptions);
253
- this.handleLoad(content);
254
- });
255
- __publicField(this, "handleError", (error) => {
256
- const { onError } = this.props;
257
- const status = error.message === "Browser does not support SVG" ? STATUS.UNSUPPORTED : STATUS.FAILED;
258
- if (this.isActive) {
259
- this.setState({ status }, () => {
260
- if (typeof onError === "function") {
261
- onError(error);
262
- }
263
- });
264
- }
265
- });
266
- __publicField(this, "handleLoad", (content, hasCache = false) => {
267
- if (this.isActive) {
268
- this.setState(
269
- {
270
- content,
271
- isCached: hasCache,
272
- status: STATUS.LOADED
273
- },
274
- this.getElement
275
- );
276
- }
277
- });
278
- this.state = {
279
- content: "",
280
- element: null,
281
- isCached: !!props.cacheRequests && cacheStore.isCached(props.src),
282
- status: STATUS.IDLE
283
- };
284
- this.hash = props.uniqueHash ?? randomString(8);
254
+ function updateSVGAttributes(node, options) {
255
+ const { baseURL = "", hash, uniquifyIDs } = options;
256
+ const replaceableAttributes = ["id", "href", "xlink:href", "xlink:role", "xlink:arcrole"];
257
+ const linkAttributes = ["href", "xlink:href"];
258
+ const isDataValue = (name, value) => linkAttributes.includes(name) && (value ? !value.includes("#") : false);
259
+ if (!uniquifyIDs) {
260
+ return node;
285
261
  }
286
- componentDidMount() {
287
- this.isActive = true;
288
- if (!canUseDOM() || this.isInitialized) {
289
- return;
290
- }
291
- const { status } = this.state;
292
- const { src } = this.props;
293
- try {
294
- if (status === STATUS.IDLE) {
295
- if (!isSupportedEnvironment()) {
296
- throw new Error("Browser does not support SVG");
262
+ [...node.children].forEach((d) => {
263
+ if (d.attributes?.length) {
264
+ const attributes = Object.values(d.attributes).map((a) => {
265
+ const attribute = a;
266
+ const match = /url\((.*?)\)/.exec(a.value);
267
+ if (match?.[1]) {
268
+ attribute.value = a.value.replace(match[0], `url(${baseURL}${match[1]}__${hash})`);
297
269
  }
298
- if (!src) {
299
- throw new Error("Missing src");
270
+ return attribute;
271
+ });
272
+ replaceableAttributes.forEach((r) => {
273
+ const attribute = attributes.find((a) => a.name === r);
274
+ if (attribute && !isDataValue(r, attribute.value)) {
275
+ attribute.value = `${attribute.value}__${hash}`;
300
276
  }
301
- this.load();
302
- }
303
- } catch (error) {
304
- this.handleError(error);
277
+ });
305
278
  }
306
- this.isInitialized = true;
307
- }
308
- componentDidUpdate(previousProps, previousState) {
309
- if (!canUseDOM()) {
310
- return;
279
+ if (d.children.length) {
280
+ return updateSVGAttributes(d, options);
311
281
  }
312
- const { isCached, status } = this.state;
313
- const { description, onLoad, src, title } = this.props;
314
- if (previousState.status !== STATUS.READY && status === STATUS.READY) {
315
- if (onLoad) {
316
- onLoad(src, isCached);
317
- }
282
+ return d;
283
+ });
284
+ return node;
285
+ }
286
+ function getNode(options) {
287
+ const {
288
+ baseURL,
289
+ content,
290
+ description,
291
+ handleError,
292
+ hash,
293
+ preProcessor,
294
+ title,
295
+ uniquifyIDs = false
296
+ } = options;
297
+ try {
298
+ const svgText = processSVG(content, preProcessor);
299
+ const node = (0, import_react_from_dom.default)(svgText, { nodeOnly: true });
300
+ if (!node || !(node instanceof SVGSVGElement)) {
301
+ throw new Error("Could not convert the src to a DOM Node");
318
302
  }
319
- if (previousProps.src !== src) {
320
- if (!src) {
321
- this.handleError(new Error("Missing src"));
322
- return;
303
+ const svg = updateSVGAttributes(node, { baseURL, hash, uniquifyIDs });
304
+ if (description) {
305
+ const originalDesc = svg.querySelector("desc");
306
+ if (originalDesc?.parentNode) {
307
+ originalDesc.parentNode.removeChild(originalDesc);
323
308
  }
324
- this.load();
309
+ const descElement = document.createElementNS("http://www.w3.org/2000/svg", "desc");
310
+ descElement.innerHTML = description;
311
+ svg.prepend(descElement);
325
312
  }
326
- if (previousProps.title !== title || previousProps.description !== description) {
327
- this.getElement();
313
+ if (typeof title !== "undefined") {
314
+ const originalTitle = svg.querySelector("title");
315
+ if (originalTitle?.parentNode) {
316
+ originalTitle.parentNode.removeChild(originalTitle);
317
+ }
318
+ if (title) {
319
+ const titleElement = document.createElementNS("http://www.w3.org/2000/svg", "title");
320
+ titleElement.innerHTML = title;
321
+ svg.prepend(titleElement);
322
+ }
328
323
  }
324
+ return svg;
325
+ } catch (error) {
326
+ return handleError(error);
329
327
  }
330
- componentWillUnmount() {
331
- this.isActive = false;
328
+ }
329
+ function processSVG(content, preProcessor) {
330
+ if (preProcessor) {
331
+ return preProcessor(content);
332
332
  }
333
- getElement() {
333
+ return content;
334
+ }
335
+ function ReactInlineSVG(props) {
336
+ const {
337
+ cacheRequests = true,
338
+ children = null,
339
+ description,
340
+ fetchOptions,
341
+ innerRef,
342
+ loader = null,
343
+ onError,
344
+ onLoad,
345
+ src,
346
+ title,
347
+ uniqueHash
348
+ } = props;
349
+ const [state, setState] = (0, import_react2.useReducer)(
350
+ (previousState2, nextState) => ({
351
+ ...previousState2,
352
+ ...nextState
353
+ }),
354
+ {
355
+ content: "",
356
+ element: null,
357
+ isCached: cacheRequests && cacheStore.isCached(props.src),
358
+ status: STATUS.IDLE
359
+ }
360
+ );
361
+ const { content, element, isCached, status } = state;
362
+ const previousProps = usePrevious(props);
363
+ const previousState = usePrevious(state);
364
+ const hash = (0, import_react2.useRef)(uniqueHash ?? randomString(8));
365
+ const isActive = (0, import_react2.useRef)(false);
366
+ const isInitialized = (0, import_react2.useRef)(false);
367
+ const handleError = (0, import_react2.useCallback)(
368
+ (error) => {
369
+ if (isActive.current) {
370
+ setState({
371
+ status: error.message === "Browser does not support SVG" ? STATUS.UNSUPPORTED : STATUS.FAILED
372
+ });
373
+ onError?.(error);
374
+ }
375
+ },
376
+ [onError]
377
+ );
378
+ const handleLoad = (0, import_react2.useCallback)((loadedContent, hasCache = false) => {
379
+ if (isActive.current) {
380
+ setState({
381
+ content: loadedContent,
382
+ isCached: hasCache,
383
+ status: STATUS.LOADED
384
+ });
385
+ }
386
+ }, []);
387
+ const getElement = (0, import_react2.useCallback)(() => {
334
388
  try {
335
- const node = this.getNode();
336
- const element = (0, import_react_from_dom.default)(node);
337
- if (!element || !React.isValidElement(element)) {
389
+ const node = getNode({ ...props, handleError, hash: hash.current, content });
390
+ const convertedElement = (0, import_react_from_dom.default)(node);
391
+ if (!convertedElement || !(0, import_react2.isValidElement)(convertedElement)) {
338
392
  throw new Error("Could not convert the src to a React element");
339
393
  }
340
- this.setState({
341
- element,
394
+ setState({
395
+ element: convertedElement,
342
396
  status: STATUS.READY
343
397
  });
344
398
  } catch (error) {
345
- this.handleError(new Error(error.message));
399
+ handleError(new Error(error.message));
400
+ }
401
+ }, [content, handleError, props]);
402
+ const fetchContent = (0, import_react2.useCallback)(async () => {
403
+ const responseContent = await request(src, fetchOptions);
404
+ handleLoad(responseContent);
405
+ }, [fetchOptions, handleLoad, src]);
406
+ const getContent = (0, import_react2.useCallback)(async () => {
407
+ const dataURI = /^data:image\/svg[^,]*?(;base64)?,(.*)/u.exec(src);
408
+ let inlineSrc;
409
+ if (dataURI) {
410
+ inlineSrc = dataURI[1] ? window.atob(dataURI[2]) : decodeURIComponent(dataURI[2]);
411
+ } else if (src.includes("<svg")) {
412
+ inlineSrc = src;
413
+ }
414
+ if (inlineSrc) {
415
+ handleLoad(inlineSrc);
416
+ return;
346
417
  }
347
- }
348
- getNode() {
349
- const { description, title } = this.props;
350
418
  try {
351
- const svgText = this.processSVG();
352
- const node = (0, import_react_from_dom.default)(svgText, { nodeOnly: true });
353
- if (!node || !(node instanceof SVGSVGElement)) {
354
- throw new Error("Could not convert the src to a DOM Node");
419
+ if (cacheRequests) {
420
+ const cachedContent = await cacheStore.get(src, fetchOptions);
421
+ handleLoad(cachedContent, true);
422
+ } else {
423
+ await fetchContent();
355
424
  }
356
- const svg = this.updateSVGAttributes(node);
357
- if (description) {
358
- const originalDesc = svg.querySelector("desc");
359
- if (originalDesc?.parentNode) {
360
- originalDesc.parentNode.removeChild(originalDesc);
361
- }
362
- const descElement = document.createElementNS("http://www.w3.org/2000/svg", "desc");
363
- descElement.innerHTML = description;
364
- svg.prepend(descElement);
365
- }
366
- if (typeof title !== "undefined") {
367
- const originalTitle = svg.querySelector("title");
368
- if (originalTitle?.parentNode) {
369
- originalTitle.parentNode.removeChild(originalTitle);
370
- }
371
- if (title) {
372
- const titleElement = document.createElementNS("http://www.w3.org/2000/svg", "title");
373
- titleElement.innerHTML = title;
374
- svg.prepend(titleElement);
375
- }
376
- }
377
- return svg;
378
425
  } catch (error) {
379
- return this.handleError(error);
426
+ handleError(error);
380
427
  }
381
- }
382
- load() {
383
- if (this.isActive) {
384
- this.setState(
385
- {
386
- content: "",
387
- element: null,
388
- isCached: false,
389
- status: STATUS.LOADING
390
- },
391
- async () => {
392
- const { cacheRequests, fetchOptions, src } = this.props;
393
- const dataURI = /^data:image\/svg[^,]*?(;base64)?,(.*)/u.exec(src);
394
- let inlineSrc;
395
- if (dataURI) {
396
- inlineSrc = dataURI[1] ? window.atob(dataURI[2]) : decodeURIComponent(dataURI[2]);
397
- } else if (src.includes("<svg")) {
398
- inlineSrc = src;
399
- }
400
- if (inlineSrc) {
401
- this.handleLoad(inlineSrc);
402
- return;
428
+ }, [cacheRequests, fetchContent, fetchOptions, handleError, handleLoad, src]);
429
+ const load = (0, import_react2.useCallback)(async () => {
430
+ if (isActive.current) {
431
+ setState({
432
+ content: "",
433
+ element: null,
434
+ isCached: false,
435
+ status: STATUS.LOADING
436
+ });
437
+ }
438
+ }, []);
439
+ (0, import_react2.useEffect)(
440
+ () => {
441
+ isActive.current = true;
442
+ if (!canUseDOM() || isInitialized.current) {
443
+ return () => void 0;
444
+ }
445
+ try {
446
+ if (status === STATUS.IDLE) {
447
+ if (!isSupportedEnvironment()) {
448
+ throw new Error("Browser does not support SVG");
403
449
  }
404
- try {
405
- if (cacheRequests) {
406
- const content = await cacheStore.get(src, fetchOptions);
407
- this.handleLoad(content, true);
408
- } else {
409
- await this.fetchContent();
410
- }
411
- } catch (error) {
412
- this.handleError(error);
450
+ if (!src) {
451
+ throw new Error("Missing src");
413
452
  }
453
+ load();
414
454
  }
415
- );
455
+ } catch (error) {
456
+ handleError(error);
457
+ }
458
+ isInitialized.current = true;
459
+ return () => {
460
+ isActive.current = false;
461
+ };
462
+ },
463
+ // eslint-disable-next-line react-hooks/exhaustive-deps
464
+ []
465
+ );
466
+ (0, import_react2.useEffect)(() => {
467
+ if (!canUseDOM()) {
468
+ return;
416
469
  }
417
- }
418
- processSVG() {
419
- const { content } = this.state;
420
- const { preProcessor } = this.props;
421
- if (preProcessor) {
422
- return preProcessor(content);
470
+ if (!previousProps) {
471
+ return;
423
472
  }
424
- return content;
425
- }
426
- updateSVGAttributes(node) {
427
- const { baseURL = "", uniquifyIDs } = this.props;
428
- const replaceableAttributes = ["id", "href", "xlink:href", "xlink:role", "xlink:arcrole"];
429
- const linkAttributes = ["href", "xlink:href"];
430
- const isDataValue = (name, value) => linkAttributes.includes(name) && (value ? !value.includes("#") : false);
431
- if (!uniquifyIDs) {
432
- return node;
433
- }
434
- [...node.children].forEach((d) => {
435
- if (d.attributes?.length) {
436
- const attributes = Object.values(d.attributes).map((a) => {
437
- const attribute = a;
438
- const match = /url\((.*?)\)/.exec(a.value);
439
- if (match?.[1]) {
440
- attribute.value = a.value.replace(match[0], `url(${baseURL}${match[1]}__${this.hash})`);
441
- }
442
- return attribute;
443
- });
444
- replaceableAttributes.forEach((r) => {
445
- const attribute = attributes.find((a) => a.name === r);
446
- if (attribute && !isDataValue(r, attribute.value)) {
447
- attribute.value = `${attribute.value}__${this.hash}`;
448
- }
449
- });
450
- }
451
- if (d.children.length) {
452
- return this.updateSVGAttributes(d);
473
+ if (previousProps.src !== src) {
474
+ if (!src) {
475
+ handleError(new Error("Missing src"));
476
+ return;
453
477
  }
454
- return d;
455
- });
456
- return node;
457
- }
458
- render() {
459
- const { element, status } = this.state;
460
- const { children = null, innerRef, loader = null } = this.props;
461
- const elementProps = omit(
462
- this.props,
463
- "baseURL",
464
- "cacheRequests",
465
- "children",
466
- "description",
467
- "fetchOptions",
468
- "innerRef",
469
- "loader",
470
- "onError",
471
- "onLoad",
472
- "preProcessor",
473
- "src",
474
- "title",
475
- "uniqueHash",
476
- "uniquifyIDs"
477
- );
478
- if (!canUseDOM()) {
479
- return loader;
478
+ load();
479
+ } else if (previousProps.title !== title || previousProps.description !== description) {
480
+ getElement();
481
+ }
482
+ }, [
483
+ description,
484
+ getElement,
485
+ handleError,
486
+ isCached,
487
+ load,
488
+ onLoad,
489
+ previousProps,
490
+ previousState,
491
+ src,
492
+ status,
493
+ title
494
+ ]);
495
+ (0, import_react2.useEffect)(() => {
496
+ if (!previousState) {
497
+ return;
480
498
  }
481
- if (element) {
482
- return React.cloneElement(element, { ref: innerRef, ...elementProps });
499
+ if (previousState.status !== STATUS.LOADING && status === STATUS.LOADING) {
500
+ getContent();
483
501
  }
484
- if ([STATUS.UNSUPPORTED, STATUS.FAILED].includes(status)) {
485
- return children;
502
+ if (previousState.status !== STATUS.LOADED && status === STATUS.LOADED) {
503
+ getElement();
504
+ }
505
+ if (previousState.status !== STATUS.READY && status === STATUS.READY) {
506
+ onLoad?.(src, isCached);
486
507
  }
508
+ }, [getContent, getElement, isCached, onLoad, previousState, src, status]);
509
+ const elementProps = omit(
510
+ props,
511
+ "baseURL",
512
+ "cacheRequests",
513
+ "children",
514
+ "description",
515
+ "fetchOptions",
516
+ "innerRef",
517
+ "loader",
518
+ "onError",
519
+ "onLoad",
520
+ "preProcessor",
521
+ "src",
522
+ "title",
523
+ "uniqueHash",
524
+ "uniquifyIDs"
525
+ );
526
+ if (!canUseDOM()) {
487
527
  return loader;
488
528
  }
489
- };
490
- __publicField(ReactInlineSVG, "defaultProps", {
491
- cacheRequests: true,
492
- uniquifyIDs: false
493
- });
529
+ if (element) {
530
+ return (0, import_react2.cloneElement)(element, { ref: innerRef, ...elementProps });
531
+ }
532
+ if ([STATUS.UNSUPPORTED, STATUS.FAILED].includes(status)) {
533
+ return children;
534
+ }
535
+ return loader;
536
+ }
494
537
  function InlineSVG(props) {
495
538
  if (!cacheStore) {
496
539
  cacheStore = new CacheStore();
497
540
  }
498
541
  const { loader } = props;
499
- const hasCallback = React.useRef(false);
500
- const [isReady, setReady] = React.useState(cacheStore.isReady);
501
- React.useEffect(() => {
542
+ const hasCallback = (0, import_react2.useRef)(false);
543
+ const [isReady, setReady] = (0, import_react2.useState)(cacheStore.isReady);
544
+ (0, import_react2.useEffect)(() => {
502
545
  if (!hasCallback.current) {
503
546
  cacheStore.onReady(() => {
504
547
  setReady(true);