Skip to content

Commit 14c15be

Browse files
committed
fix: markdown renderers
Signed-off-by: Innei <tukon479@gmail.com>
1 parent 60c248b commit 14c15be

File tree

10 files changed

+444
-368
lines changed

10 files changed

+444
-368
lines changed

packages/kami-design/components/CodeBlock/index.tsx

Lines changed: 0 additions & 18 deletions
This file was deleted.
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
.placeholder-image {
2+
@apply h-full w-full absolute max-w-full bg-current;
3+
4+
z-index: 1;
5+
border-radius: 0.5rem;
6+
transition: opacity 0.5s, height 0.2s, width 0.2s;
7+
top: 0;
8+
left: 0;
9+
display: flex;
10+
align-items: center;
11+
justify-content: center;
12+
flex-direction: column;
13+
14+
:global(html.dark) & {
15+
color: #333;
16+
}
17+
}
18+
19+
.placeholder-image.hide {
20+
opacity: 0;
21+
}
22+
23+
.image-hide {
24+
display: none;
25+
}
26+
27+
.lazyload-image {
28+
animation: blur 0.8s forwards ease-in-out;
29+
30+
&[data-status='loaded'] + .placeholder-image {
31+
@apply invisible;
32+
33+
opacity: 0;
34+
}
35+
}
36+
37+
:global(html.dark) .lazyload-image {
38+
animation: blur-dark 0.8s forwards ease-in-out;
39+
}
40+
41+
.img-alt {
42+
text-align: center;
43+
font-size: 14px;
44+
color: var(--gary);
45+
user-select: none;
46+
position: relative;
47+
margin: 12px 0;
48+
49+
&::before {
50+
content: '⇡ ';
51+
}
52+
53+
&::after {
54+
content: '';
55+
bottom: -6px;
56+
position: absolute;
57+
width: 3rem;
58+
left: 50%;
59+
transform: translateX(-50%);
60+
background: var(--gray);
61+
height: 1px;
62+
}
63+
}
64+
65+
@keyframes blur {
66+
{
67+
0% {
68+
opacity: 0;
69+
filter: brightness(1) blur(20px);
70+
}
71+
10% {
72+
opacity: 1;
73+
filter: brightness(2) blur(10px);
74+
}
75+
100% {
76+
opacity: 1;
77+
filter: brightness(1) blur(0);
78+
}
79+
}
80+
}
81+
82+
@keyframes blur-dark {
83+
{
84+
0% {
85+
opacity: 0;
86+
filter: brightness(1) blur(20px);
87+
}
88+
10% {
89+
opacity: 1;
90+
filter: brightness(0.5) blur(10px);
91+
}
92+
100% {
93+
opacity: 1;
94+
filter: brightness(1) blur(0);
95+
}
96+
}
97+
}
Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
import { clsx } from 'clsx'
2+
import mediumZoom from 'medium-zoom'
3+
import type {
4+
CSSProperties,
5+
DetailedHTMLProps,
6+
FC,
7+
ImgHTMLAttributes,
8+
} from 'react'
9+
import {
10+
forwardRef,
11+
memo,
12+
useCallback,
13+
useEffect,
14+
useImperativeHandle,
15+
useMemo,
16+
useRef,
17+
useState,
18+
} from 'react'
19+
20+
import { isDarkColorHex } from '~/utils/color'
21+
import { escapeHTMLTag } from '~/utils/utils'
22+
23+
import { LazyLoad } from '../Lazyload'
24+
import styles from './index.module.css'
25+
import { useCalculateSize } from './use-calculate-size'
26+
27+
interface ImageProps {
28+
defaultImage?: string
29+
src: string
30+
alt?: string
31+
height?: number | string
32+
width?: number | string
33+
backgroundColor?: string
34+
popup?: boolean
35+
overflowHidden?: boolean
36+
getParentElWidth?: ((parentElementWidth: number) => number) | number
37+
}
38+
39+
const Image: FC<
40+
{
41+
popup?: boolean
42+
43+
loaderFn: () => void
44+
loaded: boolean
45+
} & Pick<
46+
DetailedHTMLProps<ImgHTMLAttributes<HTMLImageElement>, HTMLImageElement>,
47+
'src' | 'alt'
48+
>
49+
> = memo(({ src, alt, popup = false, loaded, loaderFn }) => {
50+
const imageRef = useRef<HTMLImageElement>(null)
51+
52+
useEffect(() => {
53+
if (!popup) {
54+
return
55+
}
56+
const $image = imageRef.current
57+
if ($image) {
58+
const zoom = mediumZoom($image, {
59+
background: 'var(--light-bg)',
60+
})
61+
62+
return () => {
63+
zoom.detach(zoom.getImages())
64+
}
65+
}
66+
}, [popup])
67+
68+
useEffect(() => {
69+
loaderFn()
70+
}, [loaderFn])
71+
72+
return (
73+
<>
74+
<div
75+
className={clsx(
76+
styles['lazyload-image'],
77+
!loaded && styles['image-hide'],
78+
)}
79+
data-status={loaded ? 'loaded' : 'loading'}
80+
>
81+
<img src={src} alt={alt} ref={imageRef} loading="lazy" />
82+
</div>
83+
</>
84+
)
85+
})
86+
87+
export type ImageLazyRef = { status: 'loading' | 'loaded' }
88+
89+
export const ImageLazy = memo(
90+
forwardRef<
91+
ImageLazyRef,
92+
ImageProps &
93+
DetailedHTMLProps<ImgHTMLAttributes<HTMLImageElement>, HTMLImageElement>
94+
>((props, ref) => {
95+
const {
96+
defaultImage,
97+
src,
98+
alt,
99+
height,
100+
width,
101+
backgroundColor = 'rgb(111,111,111)',
102+
popup = false,
103+
style,
104+
overflowHidden = false,
105+
getParentElWidth = (w) => w,
106+
...rest
107+
} = props
108+
useImperativeHandle(ref, () => {
109+
return {
110+
status: loaded ? 'loaded' : ('loading' as any),
111+
}
112+
})
113+
const realImageRef = useRef<HTMLImageElement>(null)
114+
const placeholderRef = useRef<HTMLDivElement>(null)
115+
116+
const wrapRef = useRef<HTMLDivElement>(null)
117+
const [calculatedSize, calculateDimensions] = useCalculateSize()
118+
119+
const [loaded, setLoad] = useState(false)
120+
const loaderFn = useCallback(() => {
121+
if (!src || loaded) {
122+
return
123+
}
124+
125+
const image = new window.Image()
126+
image.src = src as string
127+
// FIXME
128+
const parentElement = wrapRef.current?.parentElement?.parentElement
129+
130+
if (!height && !width) {
131+
calculateDimensions(
132+
image,
133+
typeof getParentElWidth == 'function'
134+
? getParentElWidth(
135+
parentElement
136+
? parseFloat(getComputedStyle(parentElement).width)
137+
: 0,
138+
)
139+
: getParentElWidth,
140+
)
141+
}
142+
143+
image.onload = () => {
144+
setLoad(true)
145+
try {
146+
if (placeholderRef && placeholderRef.current) {
147+
placeholderRef.current.classList.add('hide')
148+
}
149+
150+
// eslint-disable-next-line no-empty
151+
} catch {}
152+
}
153+
image.onerror = () => {
154+
try {
155+
if (placeholderRef && placeholderRef.current) {
156+
placeholderRef.current.innerHTML = `<p style="color:${
157+
isDarkColorHex(backgroundColor) ? '#eee' : '#333'
158+
};z-index:2"><span>图片加载失败!</span><br/>
159+
<a style="margin: 0 12px;word-break:break-all;white-space:pre-wrap;display:inline-block;" href="${escapeHTMLTag(
160+
image.src,
161+
)}" target="_blank">${escapeHTMLTag(image.src)}</a></p>`
162+
}
163+
// eslint-disable-next-line no-empty
164+
} catch {}
165+
}
166+
}, [
167+
src,
168+
loaded,
169+
height,
170+
width,
171+
calculateDimensions,
172+
getParentElWidth,
173+
backgroundColor,
174+
])
175+
const memoPlaceholderImage = useMemo(
176+
() => (
177+
<PlaceholderImage
178+
height={height}
179+
width={width}
180+
backgroundColor={backgroundColor}
181+
ref={placeholderRef}
182+
/>
183+
),
184+
[backgroundColor, height, width],
185+
)
186+
187+
const imageWrapperStyle = useMemo<CSSProperties>(
188+
() => ({
189+
height: loaded ? undefined : height || calculatedSize.height,
190+
width: loaded ? undefined : width || calculatedSize.width,
191+
192+
...(overflowHidden ? { overflow: 'hidden', borderRadius: '3px' } : {}),
193+
}),
194+
[
195+
calculatedSize.height,
196+
calculatedSize.width,
197+
height,
198+
loaded,
199+
overflowHidden,
200+
width,
201+
],
202+
)
203+
return (
204+
<figure style={style} className="inline-block">
205+
{defaultImage ? (
206+
<img src={defaultImage} alt={alt} {...rest} ref={realImageRef} />
207+
) : (
208+
<div
209+
className={clsx(
210+
'transition-none relative max-w-full m-auto inline-block min-h-[1px]',
211+
rest.className,
212+
)}
213+
style={imageWrapperStyle}
214+
ref={wrapRef}
215+
data-info={JSON.stringify({ height, width, calculatedSize })}
216+
>
217+
<LazyLoad offset={100} placeholder={memoPlaceholderImage}>
218+
<Image
219+
src={src}
220+
alt={alt}
221+
popup={popup}
222+
loaded={loaded}
223+
loaderFn={loaderFn}
224+
/>
225+
{!loaded && memoPlaceholderImage}
226+
</LazyLoad>
227+
</div>
228+
)}
229+
{alt && <figcaption className={styles['img-alt']}>{alt}</figcaption>}
230+
</figure>
231+
)
232+
}),
233+
)
234+
235+
const PlaceholderImage = memo(
236+
forwardRef<
237+
HTMLDivElement,
238+
{ ref: any; className?: string } & Partial<ImageProps>
239+
>((props, ref) => {
240+
const { backgroundColor, height, width } = props
241+
return (
242+
<div
243+
className={clsx(styles['placeholder-image'], props.className)}
244+
ref={ref}
245+
style={{
246+
height,
247+
width,
248+
color: backgroundColor,
249+
}}
250+
/>
251+
)
252+
}),
253+
)

0 commit comments

Comments
 (0)