react-svg
Version:
A React component that injects SVG into the DOM.
205 lines (204 loc) • 7.18 kB
JavaScript
"use client";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
//#region \0rolldown/runtime.js
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
key = keys[i];
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
get: ((k) => from[k]).bind(null, key),
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
});
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
value: mod,
enumerable: true
}) : target, mod));
//#endregion
let _tanem_svg_injector = require("@tanem/svg-injector");
let react = require("react");
react = __toESM(react);
//#region src/owner-window.ts
const ownerWindow = (node) => {
return ((node === null || node === void 0 ? void 0 : node.ownerDocument) || document).defaultView || window;
};
//#endregion
//#region src/ReactSVG.tsx
const svgNamespace = "http://www.w3.org/2000/svg";
const xlinkNamespace = "http://www.w3.org/1999/xlink";
const idPrefix = `react-svg-${Math.random().toString(36).slice(2, 6)}`;
let idCounter = 0;
const ReactSVG = react.forwardRef(({ afterInjection = () => void 0, beforeInjection = () => void 0, desc = "", evalScripts = "never", fallback: Fallback, httpRequestWithCredentials = false, loading: Loading, loadingDelay = 0, onError = () => void 0, renumerateIRIElements = true, src, title = "", useRequestCache = true, wrapper = "div", ...rest }, forwardedRef) => {
const [hasError, setHasError] = react.useState(false);
const [isLoading, setIsLoading] = react.useState(true);
const [hasLoadingDelayElapsed, setHasLoadingDelayElapsed] = react.useState(loadingDelay <= 0);
const [injectionId, setInjectionId] = react.useState(0);
const hasInjectedRef = react.useRef(false);
const reactWrapperRef = react.useRef(null);
const callbacksRef = react.useRef({
afterInjection,
beforeInjection,
onError
});
react.useEffect(() => {
callbacksRef.current = {
afterInjection,
beforeInjection,
onError
};
});
const loadingDelayRef = react.useRef(loadingDelay);
react.useEffect(() => {
loadingDelayRef.current = loadingDelay;
});
const refCallback = react.useCallback((reactWrapper) => {
reactWrapperRef.current = reactWrapper;
if (typeof forwardedRef === "function") forwardedRef(reactWrapper);
else if (forwardedRef) forwardedRef.current = reactWrapper;
}, [forwardedRef]);
react.useEffect(() => {
const reactWrapper = reactWrapperRef.current;
/* istanbul ignore next */
if (!(reactWrapper instanceof ownerWindow(reactWrapper).Node)) return;
let isActive = true;
let nonReactWrapper = null;
const removeSVG = () => {
if (nonReactWrapper === null || nonReactWrapper === void 0 ? void 0 : nonReactWrapper.parentNode) {
nonReactWrapper.parentNode.removeChild(nonReactWrapper);
nonReactWrapper = null;
}
};
setHasError(false);
setIsLoading(true);
setHasLoadingDelayElapsed(loadingDelayRef.current <= 0);
if (hasInjectedRef.current) setInjectionId((id) => id + 1);
else hasInjectedRef.current = true;
let nonReactTarget;
if (wrapper === "svg") {
nonReactWrapper = document.createElementNS(svgNamespace, wrapper);
nonReactWrapper.setAttribute("xmlns", svgNamespace);
nonReactWrapper.setAttribute("xmlns:xlink", xlinkNamespace);
nonReactTarget = document.createElementNS(svgNamespace, wrapper);
} else {
nonReactWrapper = document.createElement(wrapper);
nonReactTarget = document.createElement(wrapper);
}
nonReactWrapper.appendChild(nonReactTarget);
nonReactTarget.dataset.src = src;
reactWrapper.appendChild(nonReactWrapper);
const handleError = (error) => {
removeSVG();
if (isActive) {
setHasError(true);
setIsLoading(false);
}
callbacksRef.current.onError(error);
};
const afterEach = (error, svg) => {
if (error) {
handleError(error);
return;
}
if (!isActive) return;
setIsLoading(false);
try {
callbacksRef.current.afterInjection(svg);
} catch (afterInjectionError) {
handleError(afterInjectionError);
}
};
const beforeEach = (svg) => {
svg.setAttribute("role", "img");
const ariaLabelledBy = [];
const ariaDescribedBy = [];
if (title) {
const originalTitle = svg.querySelector(":scope > title");
if (originalTitle) svg.removeChild(originalTitle);
const titleId = `${idPrefix}-title-${++idCounter}`;
const newTitle = document.createElementNS(svgNamespace, "title");
newTitle.id = titleId;
newTitle.textContent = title;
svg.prepend(newTitle);
ariaLabelledBy.push(titleId);
}
if (desc) {
const originalDesc = svg.querySelector(":scope > desc");
if (originalDesc) svg.removeChild(originalDesc);
const descId = `${idPrefix}-desc-${++idCounter}`;
const newDesc = document.createElementNS(svgNamespace, "desc");
newDesc.id = descId;
newDesc.textContent = desc;
const existingTitle = svg.querySelector(":scope > title");
if (existingTitle) existingTitle.after(newDesc);
else svg.prepend(newDesc);
ariaDescribedBy.push(descId);
}
if (ariaLabelledBy.length > 0) svg.setAttribute("aria-labelledby", ariaLabelledBy.join(" "));
if (ariaDescribedBy.length > 0) svg.setAttribute("aria-describedby", ariaDescribedBy.join(" "));
try {
callbacksRef.current.beforeInjection(svg);
} catch (error) {
handleError(error);
}
};
(0, _tanem_svg_injector.SVGInjector)(nonReactTarget, {
afterEach,
beforeEach,
cacheRequests: useRequestCache,
evalScripts,
httpRequestWithCredentials,
renumerateIRIElements
});
return () => {
isActive = false;
removeSVG();
};
}, [
desc,
evalScripts,
httpRequestWithCredentials,
renumerateIRIElements,
src,
title,
useRequestCache,
wrapper
]);
react.useEffect(() => {
if (!isLoading) return;
if (loadingDelay <= 0) {
setHasLoadingDelayElapsed(true);
return;
}
setHasLoadingDelayElapsed(false);
const timeoutId = setTimeout(() => {
setHasLoadingDelayElapsed(true);
}, loadingDelay);
return () => {
clearTimeout(timeoutId);
};
}, [
injectionId,
isLoading,
loadingDelay
]);
const Wrapper = wrapper;
return /* @__PURE__ */ react.createElement(Wrapper, {
...rest,
ref: refCallback,
...wrapper === "svg" ? {
xmlns: svgNamespace,
xmlnsXlink: xlinkNamespace
} : {}
}, isLoading && hasLoadingDelayElapsed && Loading && /* @__PURE__ */ react.createElement(Loading, null), hasError && Fallback && /* @__PURE__ */ react.createElement(Fallback, null));
});
ReactSVG.displayName = "ReactSVG";
//#endregion
exports.ReactSVG = ReactSVG;
//# sourceMappingURL=react-svg.cjs.map