Skip to content

Commit c4b9c65

Browse files
authored
chore: extract svg from image (#4727)
1 parent 04a81db commit c4b9c65

File tree

3 files changed

+83
-0
lines changed

3 files changed

+83
-0
lines changed

packages/fern-docs/bundle/src/components/util/processIcon.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { NavigationNode } from "@fern-api/fdr-sdk/navigation";
33
import { hasMetadata } from "@fern-api/fdr-sdk/navigation";
44
import { NoZoom } from "@fern-docs/components/contexts/NoZoom";
55
import { FernImage } from "@fern-docs/components/FernImage";
6+
import { FernSvgIcon } from "@fern-docs/components/FernSvgIcon";
67
import type { ReactNode } from "react";
78
import { FaIconServer } from "../fa-icon-server";
89

@@ -22,6 +23,14 @@ export const processIcon = ({ node, fallback, files }: ProcessIconOptions): Reac
2223
const fileData = files?.[fileId];
2324

2425
if (fileData) {
26+
if (fileData.src.endsWith(".svg")) {
27+
return (
28+
<NoZoom>
29+
<FernSvgIcon src={fileData.src} alt={fileData.alt ?? ""} className="fern-file-icon size-5" />
30+
</NoZoom>
31+
);
32+
}
33+
2534
return (
2635
<NoZoom>
2736
<FernImage
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
"use client";
2+
3+
import Image from "next/image";
4+
import { useEffect, useState } from "react";
5+
6+
interface FernSvgIconProps {
7+
src: string;
8+
alt?: string;
9+
className?: string;
10+
}
11+
12+
export const FernSvgIcon: React.FC<FernSvgIconProps> = ({ src, alt, className }) => {
13+
const [svgContent, setSvgContent] = useState<string | null>(null);
14+
const [error, setError] = useState<boolean>(false);
15+
16+
useEffect(() => {
17+
let isMounted = true;
18+
19+
const fetchSvg = async () => {
20+
try {
21+
// fetch icon from s3
22+
const response = await fetch(src);
23+
if (!response.ok) {
24+
throw new Error(`Failed to fetch SVG: ${response.statusText}`);
25+
}
26+
const text = await response.text();
27+
if (isMounted) {
28+
setSvgContent(text);
29+
}
30+
} catch (err) {
31+
console.error("Error fetching SVG:", err);
32+
if (isMounted) {
33+
setError(true);
34+
}
35+
}
36+
};
37+
38+
void fetchSvg();
39+
40+
return () => {
41+
isMounted = false;
42+
};
43+
}, [src]);
44+
45+
if (error) {
46+
return <Image src={src} width={16} height={16} alt={alt ?? ""} className={className} />;
47+
}
48+
49+
if (!svgContent) {
50+
return <span className={className} />;
51+
}
52+
53+
const parser = new DOMParser();
54+
const svgDoc = parser.parseFromString(svgContent, "image/svg+xml");
55+
const svgElement = svgDoc.querySelector("svg");
56+
57+
if (svgElement && className) {
58+
const existingClass = svgElement.getAttribute("class");
59+
svgElement.setAttribute("class", existingClass ? `${existingClass} ${className}` : className);
60+
}
61+
62+
const modifiedSvgContent = svgElement ? new XMLSerializer().serializeToString(svgElement) : svgContent;
63+
64+
return <span dangerouslySetInnerHTML={{ __html: modifiedSvgContent }} />;
65+
};

packages/fern-docs/components/src/processIcon.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type { ReactNode } from "react";
55

66
import { NoZoom } from "./contexts/NoZoom";
77
import { FernImage } from "./FernImage";
8+
import { FernSvgIcon } from "./FernSvgIcon";
89
import { FaIconServer } from "./fa-icon-server";
910

1011
export interface ProcessIconOptions {
@@ -35,6 +36,14 @@ export const processIcon = ({
3536
const fileData = files?.[fileId];
3637

3738
if (fileData) {
39+
if (fileData.src.endsWith(".svg")) {
40+
return (
41+
<NoZoom>
42+
<FernSvgIcon src={fileData.src} alt={fileData.alt ?? ""} className="fern-file-icon size-5" />
43+
</NoZoom>
44+
);
45+
}
46+
3847
return (
3948
<NoZoom>
4049
<FernImage

0 commit comments

Comments
 (0)