58 lines
1.5 KiB
TypeScript
58 lines
1.5 KiB
TypeScript
import { Component, createSignal, onMount, onCleanup, Show } from 'solid-js';
|
||
import { parseMarkdown } from '../markdown';
|
||
import { fetchData } from '../data-loader';
|
||
|
||
export interface ArticleProps {
|
||
src: string;
|
||
onLoaded?: () => void;
|
||
onError?: (error: Error) => void;
|
||
}
|
||
|
||
/**
|
||
* Article 组件
|
||
* 用于将特定 src 位置的 md 文件显示为 markdown 文章
|
||
*/
|
||
export const Article: Component<ArticleProps> = (props) => {
|
||
const [content, setContent] = createSignal('');
|
||
const [loading, setLoading] = createSignal(true);
|
||
const [error, setError] = createSignal<Error | null>(null);
|
||
|
||
let articleRef: HTMLArticleElement | undefined;
|
||
|
||
onMount(async () => {
|
||
setLoading(true);
|
||
try {
|
||
const text = await fetchData(props.src);
|
||
setContent(text);
|
||
setLoading(false);
|
||
props.onLoaded?.();
|
||
} catch (err) {
|
||
const errorObj = err instanceof Error ? err : new Error(String(err));
|
||
setError(errorObj);
|
||
setLoading(false);
|
||
props.onError?.(errorObj);
|
||
}
|
||
});
|
||
|
||
onCleanup(() => {
|
||
// 清理时清空内容,触发内部组件的销毁
|
||
setContent('');
|
||
});
|
||
|
||
return (
|
||
<article ref={articleRef} class="prose" data-src={props.src}>
|
||
<Show when={loading()}>
|
||
<div class="text-gray-500">加载中...</div>
|
||
</Show>
|
||
<Show when={error()}>
|
||
<div class="text-red-500">加载失败:{error()?.message}</div>
|
||
</Show>
|
||
<Show when={!loading() && !error()}>
|
||
<div innerHTML={parseMarkdown(content())} />
|
||
</Show>
|
||
</article>
|
||
);
|
||
};
|
||
|
||
export default Article;
|