mirror of https://github.com/fantasticit/think.git
feat: improve loading state content
This commit is contained in:
parent
da0f601423
commit
6113ecc101
|
@ -92,7 +92,7 @@ export const CommentEditor: React.FC<IProps> = ({ documentId }) => {
|
||||||
error={error}
|
error={error}
|
||||||
loadingContent={
|
loadingContent={
|
||||||
<>
|
<>
|
||||||
{Array.from({ length: 5 }, (_, i) => i).map((i) => (
|
{Array.from({ length: 3 }, (_, i) => i).map((i) => (
|
||||||
<CommentItemPlaceholder key={i} />
|
<CommentItemPlaceholder key={i} />
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useMemo, useEffect, useRef } from 'react';
|
import React, { useMemo, useEffect } from 'react';
|
||||||
import { useEditor, EditorContent } from '@tiptap/react';
|
import { useEditor, EditorContent } from '@tiptap/react';
|
||||||
import { Layout } from '@douyinfe/semi-ui';
|
import { Layout } from '@douyinfe/semi-ui';
|
||||||
import { IDocument, ILoginUser } from '@think/domains';
|
import { IDocument, ILoginUser } from '@think/domains';
|
||||||
|
@ -10,6 +10,7 @@ import {
|
||||||
getCollaborationCursorExtension,
|
getCollaborationCursorExtension,
|
||||||
getProvider,
|
getProvider,
|
||||||
destoryProvider,
|
destoryProvider,
|
||||||
|
DocumentSkeleton,
|
||||||
} from 'tiptap';
|
} from 'tiptap';
|
||||||
import { DataRender } from 'components/data-render';
|
import { DataRender } from 'components/data-render';
|
||||||
import { ImageViewer } from 'components/image-viewer';
|
import { ImageViewer } from 'components/image-viewer';
|
||||||
|
@ -26,8 +27,6 @@ interface IProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Editor: React.FC<IProps> = ({ user, documentId, document }) => {
|
export const Editor: React.FC<IProps> = ({ user, documentId, document }) => {
|
||||||
if (!user) return null;
|
|
||||||
|
|
||||||
const provider = useMemo(() => {
|
const provider = useMemo(() => {
|
||||||
return getProvider({
|
return getProvider({
|
||||||
targetId: documentId,
|
targetId: documentId,
|
||||||
|
@ -70,6 +69,7 @@ export const Editor: React.FC<IProps> = ({ user, documentId, document }) => {
|
||||||
return (
|
return (
|
||||||
<DataRender
|
<DataRender
|
||||||
loading={loading}
|
loading={loading}
|
||||||
|
loadingContent={<DocumentSkeleton />}
|
||||||
error={null}
|
error={null}
|
||||||
normalContent={() => {
|
normalContent={() => {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -27,6 +27,10 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.editorContainer {
|
||||||
|
min-height: 240px;
|
||||||
|
}
|
||||||
|
|
||||||
.commentWrap {
|
.commentWrap {
|
||||||
padding: 16px 0;
|
padding: 16px 0;
|
||||||
border-top: 1px solid var(--semi-color-border);
|
border-top: 1px solid var(--semi-color-border);
|
||||||
|
|
|
@ -28,14 +28,13 @@ interface IProps {
|
||||||
|
|
||||||
export const DocumentReader: React.FC<IProps> = ({ documentId }) => {
|
export const DocumentReader: React.FC<IProps> = ({ documentId }) => {
|
||||||
if (!documentId) return null;
|
if (!documentId) return null;
|
||||||
|
|
||||||
const { width: windowWidth } = useWindowSize();
|
const { width: windowWidth } = useWindowSize();
|
||||||
const { width, fontSize } = useDocumentStyle();
|
const { width, fontSize } = useDocumentStyle();
|
||||||
const editorWrapClassNames = useMemo(() => {
|
const editorWrapClassNames = useMemo(() => {
|
||||||
return width === 'standardWidth' ? styles.isStandardWidth : styles.isFullWidth;
|
return width === 'standardWidth' ? styles.isStandardWidth : styles.isFullWidth;
|
||||||
}, [width]);
|
}, [width]);
|
||||||
|
|
||||||
const { user } = useUser();
|
const { user } = useUser();
|
||||||
|
|
||||||
const { data: documentAndAuth, loading: docAuthLoading, error: docAuthError } = useDocumentDetail(documentId);
|
const { data: documentAndAuth, loading: docAuthLoading, error: docAuthError } = useDocumentDetail(documentId);
|
||||||
const { document, authority } = documentAndAuth || {};
|
const { document, authority } = documentAndAuth || {};
|
||||||
|
|
||||||
|
@ -53,13 +52,7 @@ export const DocumentReader: React.FC<IProps> = ({ documentId }) => {
|
||||||
<DataRender
|
<DataRender
|
||||||
loading={docAuthLoading}
|
loading={docAuthLoading}
|
||||||
error={docAuthError}
|
error={docAuthError}
|
||||||
loadingContent={
|
loadingContent={<Skeleton active placeholder={<Skeleton.Title style={{ width: 80 }} />} loading={true} />}
|
||||||
<Skeleton
|
|
||||||
active
|
|
||||||
placeholder={<Skeleton.Title style={{ width: 80, marginBottom: 8 }} />}
|
|
||||||
loading={true}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
normalContent={() => (
|
normalContent={() => (
|
||||||
<Text strong ellipsis={{ showTooltip: true }} style={{ width: ~~(windowWidth / 4) }}>
|
<Text strong ellipsis={{ showTooltip: true }} style={{ width: ~~(windowWidth / 4) }}>
|
||||||
{document.title}
|
{document.title}
|
||||||
|
@ -101,7 +94,9 @@ export const DocumentReader: React.FC<IProps> = ({ documentId }) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Seo title={document.title} />
|
<Seo title={document.title} />
|
||||||
<Editor key={document.id} user={user} documentId={document.id} document={document} />
|
<div className={styles.editorContainer}>
|
||||||
|
{user && <Editor key={document.id} user={user} documentId={document.id} document={document} />}
|
||||||
|
</div>
|
||||||
<div className={styles.commentWrap}>
|
<div className={styles.commentWrap}>
|
||||||
<CommentEditor documentId={document.id} />
|
<CommentEditor documentId={document.id} />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -23,7 +23,6 @@ import { User } from 'components/user';
|
||||||
import { Theme } from 'components/theme';
|
import { Theme } from 'components/theme';
|
||||||
import { ImageViewer } from 'components/image-viewer';
|
import { ImageViewer } from 'components/image-viewer';
|
||||||
import { useDocumentStyle } from 'hooks/use-document-style';
|
import { useDocumentStyle } from 'hooks/use-document-style';
|
||||||
import { useWindowSize } from 'hooks/use-window-size';
|
|
||||||
import { usePublicDocument } from 'data/document';
|
import { usePublicDocument } from 'data/document';
|
||||||
import { DocumentSkeleton } from 'tiptap';
|
import { DocumentSkeleton } from 'tiptap';
|
||||||
import { DocumentContent } from './content';
|
import { DocumentContent } from './content';
|
||||||
|
@ -41,7 +40,6 @@ export const DocumentPublicReader: React.FC<IProps> = ({ documentId, hideLogo =
|
||||||
if (!documentId) return null;
|
if (!documentId) return null;
|
||||||
|
|
||||||
const { data, loading, error, query } = usePublicDocument(documentId);
|
const { data, loading, error, query } = usePublicDocument(documentId);
|
||||||
const { width: windowWidth } = useWindowSize();
|
|
||||||
const { width, fontSize } = useDocumentStyle();
|
const { width, fontSize } = useDocumentStyle();
|
||||||
const editorWrapClassNames = useMemo(() => {
|
const editorWrapClassNames = useMemo(() => {
|
||||||
return width === 'standardWidth' ? styles.isStandardWidth : styles.isFullWidth;
|
return width === 'standardWidth' ? styles.isStandardWidth : styles.isFullWidth;
|
||||||
|
@ -100,9 +98,7 @@ export const DocumentPublicReader: React.FC<IProps> = ({ documentId, hideLogo =
|
||||||
<DataRender
|
<DataRender
|
||||||
loading={loading}
|
loading={loading}
|
||||||
error={error}
|
error={error}
|
||||||
loadingContent={
|
loadingContent={<Skeleton active placeholder={<Skeleton.Title style={{ width: 80 }} />} loading={true} />}
|
||||||
<Skeleton active placeholder={<Skeleton.Title style={{ width: 80, marginBottom: 8 }} />} loading={true} />
|
|
||||||
}
|
|
||||||
normalContent={() => (
|
normalContent={() => (
|
||||||
<Breadcrumb>
|
<Breadcrumb>
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
|
|
|
@ -216,23 +216,7 @@ export const WikiTocs: React.FC<IProps> = ({
|
||||||
<div className={styles.treeWrap}>
|
<div className={styles.treeWrap}>
|
||||||
<DataRender
|
<DataRender
|
||||||
loading={tocsLoading}
|
loading={tocsLoading}
|
||||||
loadingContent={
|
loadingContent={<NavItem icon={null} text={<Skeleton.Title style={{ width: '100%' }} />} />}
|
||||||
<NavItem
|
|
||||||
icon={
|
|
||||||
<Skeleton.Avatar
|
|
||||||
size="small"
|
|
||||||
style={{
|
|
||||||
marginRight: 8,
|
|
||||||
width: 24,
|
|
||||||
height: 24,
|
|
||||||
borderRadius: 4,
|
|
||||||
}}
|
|
||||||
></Skeleton.Avatar>
|
|
||||||
}
|
|
||||||
text={<Skeleton.Title style={{ width: 120 }} />}
|
|
||||||
rightNode={<IconPlus />}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
error={tocsError}
|
error={tocsError}
|
||||||
normalContent={() => (
|
normalContent={() => (
|
||||||
<Tree
|
<Tree
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import type { NextPage } from 'next';
|
import type { NextPage } from 'next';
|
||||||
import type { IDocument } from '@think/domains';
|
import type { IDocument } from '@think/domains';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import React from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { Typography, Button, Table, Spin, List } from '@douyinfe/semi-ui';
|
import { Typography, Button, Table, List } from '@douyinfe/semi-ui';
|
||||||
import { useToggle } from 'hooks/use-toggle';
|
import { useToggle } from 'hooks/use-toggle';
|
||||||
import { Seo } from 'components/seo';
|
import { Seo } from 'components/seo';
|
||||||
import { DataRender } from 'components/data-render';
|
import { DataRender } from 'components/data-render';
|
||||||
|
@ -31,6 +31,45 @@ const grid = {
|
||||||
const RecentDocs = () => {
|
const RecentDocs = () => {
|
||||||
const { data, error, loading, refresh } = useRecentDocuments();
|
const { data, error, loading, refresh } = useRecentDocuments();
|
||||||
|
|
||||||
|
const columns = useMemo(
|
||||||
|
() => [
|
||||||
|
<Column
|
||||||
|
title="标题"
|
||||||
|
dataIndex="title"
|
||||||
|
key="title"
|
||||||
|
render={(_, document: IDocument) => {
|
||||||
|
return (
|
||||||
|
<Link href={'/wiki/[wikiId]/document/[docId]'} as={`/wiki/${document.wikiId}/document/${document.id}`}>
|
||||||
|
<a style={{ color: 'inherit', textDecoration: 'none' }}>{document.title}</a>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>,
|
||||||
|
<Column title="阅读量" dataIndex="views" key="views" />,
|
||||||
|
<Column
|
||||||
|
title="更新时间"
|
||||||
|
dataIndex="updatedAt"
|
||||||
|
key="updatedAt"
|
||||||
|
render={(date) => <LocaleTime date={date} timeago />}
|
||||||
|
/>,
|
||||||
|
<Column
|
||||||
|
title="操作"
|
||||||
|
dataIndex="operate"
|
||||||
|
key="operate"
|
||||||
|
render={(_, document) => (
|
||||||
|
<DocumentActions
|
||||||
|
wikiId={document.wikiId}
|
||||||
|
documentId={document.id}
|
||||||
|
onStar={refresh}
|
||||||
|
onDelete={refresh}
|
||||||
|
showCreateDocument
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>,
|
||||||
|
],
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Title heading={3} style={{ margin: '24px 0 0' }}>
|
<Title heading={3} style={{ margin: '24px 0 0' }}>
|
||||||
|
@ -38,47 +77,16 @@ const RecentDocs = () => {
|
||||||
</Title>
|
</Title>
|
||||||
<DataRender
|
<DataRender
|
||||||
loading={loading}
|
loading={loading}
|
||||||
loadingContent={<Spin />}
|
loadingContent={
|
||||||
|
<Table dataSource={[]} loading={true} pagination={false} size="small" style={{ marginTop: 16 }}>
|
||||||
|
{columns}
|
||||||
|
</Table>
|
||||||
|
}
|
||||||
error={error}
|
error={error}
|
||||||
normalContent={() =>
|
normalContent={() =>
|
||||||
data && data.length ? (
|
data && data.length ? (
|
||||||
<Table dataSource={data} loading={loading} pagination={false} size="small" style={{ marginTop: 16 }}>
|
<Table dataSource={data} loading={loading} pagination={false} size="small" style={{ marginTop: 16 }}>
|
||||||
<Column
|
{columns}
|
||||||
title="标题"
|
|
||||||
dataIndex="title"
|
|
||||||
key="title"
|
|
||||||
render={(_, document: IDocument) => {
|
|
||||||
return (
|
|
||||||
<Link
|
|
||||||
href={'/wiki/[wikiId]/document/[docId]'}
|
|
||||||
as={`/wiki/${document.wikiId}/document/${document.id}`}
|
|
||||||
>
|
|
||||||
<a style={{ color: 'inherit', textDecoration: 'none' }}>{document.title}</a>
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Column title="阅读量" dataIndex="views" key="views" />
|
|
||||||
<Column
|
|
||||||
title="更新时间"
|
|
||||||
dataIndex="updatedAt"
|
|
||||||
key="updatedAt"
|
|
||||||
render={(date) => <LocaleTime date={date} timeago />}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
title="操作"
|
|
||||||
dataIndex="operate"
|
|
||||||
key="operate"
|
|
||||||
render={(_, document) => (
|
|
||||||
<DocumentActions
|
|
||||||
wikiId={document.wikiId}
|
|
||||||
documentId={document.id}
|
|
||||||
onStar={refresh}
|
|
||||||
onDelete={refresh}
|
|
||||||
showCreateDocument
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</Table>
|
</Table>
|
||||||
) : (
|
) : (
|
||||||
<Empty message="最近访问的文档会出现在此处" />
|
<Empty message="最近访问的文档会出现在此处" />
|
||||||
|
|
|
@ -4,8 +4,8 @@ import { Skeleton } from '@douyinfe/semi-ui';
|
||||||
export const DocumentSkeleton = () => {
|
export const DocumentSkeleton = () => {
|
||||||
const placeholder = (
|
const placeholder = (
|
||||||
<>
|
<>
|
||||||
<Skeleton.Title style={{ width: 240, height: '1.8em', marginBottom: 12, marginTop: 12 }} />
|
<Skeleton.Title style={{ width: 240, height: '2.4em', marginBottom: 12, marginTop: 12 }} />
|
||||||
<Skeleton.Paragraph style={{ width: '100%' }} rows={15} />
|
<Skeleton.Paragraph style={{ width: '100%', height: 27 }} rows={7} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue