|
1 | 1 | 'use client'; |
2 | 2 |
|
3 | 3 | import { ImageZoom } from 'fumadocs-ui/components/image-zoom'; |
4 | | -import { useMemo } from 'react'; |
| 4 | +import { useEffect, useMemo, useRef, useState } from 'react'; |
5 | 5 |
|
6 | 6 | const CDN_PATTERN = /^https?:\/\/images\.sealos\.run/; |
7 | 7 | const APP_URL_PLACEHOLDER = '__APP_URL__'; |
@@ -56,15 +56,35 @@ const resolveImageSrc = (src: MdxImageSrc) => { |
56 | 56 | }; |
57 | 57 |
|
58 | 58 | export function MdxImage(props: React.ImgHTMLAttributes<HTMLImageElement>) { |
59 | | - const { className, src, ...rest } = props; |
| 59 | + const { className, src, onError, ...rest } = props; |
60 | 60 | const resolvedSrc = useMemo(() => resolveImageSrc(src), [src]); |
| 61 | + const [currentSrc, setCurrentSrc] = useState<MdxImageSrc>(resolvedSrc); |
| 62 | + const [didFallback, setDidFallback] = useState(false); |
| 63 | + const originalSrcRef = useRef<MdxImageSrc>(src); |
| 64 | + const resolvedSrcRef = useRef<MdxImageSrc>(resolvedSrc); |
61 | 65 | const mergedClassName = ['rounded-xl', className].filter(Boolean).join(' '); |
62 | 66 |
|
| 67 | + useEffect(() => { |
| 68 | + originalSrcRef.current = src; |
| 69 | + resolvedSrcRef.current = resolvedSrc; |
| 70 | + setCurrentSrc(resolvedSrc); |
| 71 | + setDidFallback(false); |
| 72 | + }, [src, resolvedSrc]); |
| 73 | + |
| 74 | + const handleError: React.ReactEventHandler<HTMLImageElement> = (event) => { |
| 75 | + onError?.(event); |
| 76 | + if (didFallback) return; |
| 77 | + if (resolvedSrcRef.current === originalSrcRef.current) return; |
| 78 | + setDidFallback(true); |
| 79 | + setCurrentSrc(originalSrcRef.current); |
| 80 | + }; |
| 81 | + |
63 | 82 | return ( |
64 | 83 | <ImageZoom |
65 | 84 | {...(rest as any)} |
66 | | - src={resolvedSrc} |
| 85 | + src={currentSrc} |
67 | 86 | className={mergedClassName} |
| 87 | + onError={handleError} |
68 | 88 | /> |
69 | 89 | ); |
70 | 90 | } |
0 commit comments