mirror of https://github.com/fantasticit/think.git
feat: improve ux
This commit is contained in:
parent
59b5fc3539
commit
549aff2cd6
|
@ -5,7 +5,6 @@ import { useCreateDocument } from 'data/document';
|
|||
import { usePublicTemplates, useOwnTemplates } from 'data/template';
|
||||
import { TemplateList } from 'components/template/list';
|
||||
import { TemplateCardEmpty } from 'components/template/card';
|
||||
|
||||
import styles from './index.module.scss';
|
||||
|
||||
interface IProps {
|
||||
|
@ -56,7 +55,7 @@ export const DocumentCreator: React.FC<IProps> = ({ wikiId, parentDocumentId, vi
|
|||
width: '96vh',
|
||||
}}
|
||||
bodyStyle={{
|
||||
maxHeight: 'calc(90vh - 120px)',
|
||||
maxHeight: 'calc(80vh - 150px)',
|
||||
overflow: 'auto',
|
||||
}}
|
||||
key={wikiId}
|
||||
|
|
|
@ -32,7 +32,7 @@ const MessagesRender = ({ messageData, loading, error, onClick = null, page = 1,
|
|||
return (
|
||||
<div
|
||||
className={styles.itemsWrap}
|
||||
style={{ margin: '8px -16px' }}
|
||||
style={{ margin: '8px -16px', minHeight: 224 }}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
|
|
@ -5,7 +5,7 @@ export const Placeholder = () => {
|
|||
<Skeleton
|
||||
placeholder={
|
||||
<>
|
||||
{Array.from({ length: 6 }).fill(
|
||||
{Array.from({ length: 7 }).fill(
|
||||
<Skeleton.Title style={{ width: '100%', marginBottom: 12, marginTop: 12 }} />
|
||||
)}
|
||||
</>
|
||||
|
|
|
@ -1,19 +1,53 @@
|
|||
import React from 'react';
|
||||
import { Spin } from '@douyinfe/semi-ui';
|
||||
import React, { useState, useMemo, useCallback, useEffect } from 'react';
|
||||
import Router from 'next/router';
|
||||
import cls from 'classnames';
|
||||
import { Spin, Button, Nav, Space, Typography, Tooltip, Switch, Popover, Popconfirm } from '@douyinfe/semi-ui';
|
||||
import { IconChevronLeft, IconArticle } from '@douyinfe/semi-icons';
|
||||
import { useUser } from 'data/user';
|
||||
import { useTemplate } from 'data/template';
|
||||
import { Seo } from 'components/seo';
|
||||
import { DataRender } from 'components/data-render';
|
||||
import { useTemplate } from 'data/template';
|
||||
import { Editor } from './editor';
|
||||
import { Theme } from 'components/theme';
|
||||
import { User } from 'components/user';
|
||||
import { DocumentStyle } from 'components/document/style';
|
||||
import { useDocumentStyle } from 'hooks/use-document-style';
|
||||
import { useWindowSize } from 'hooks/use-window-size';
|
||||
import { CollaborationEditor } from 'tiptap/editor';
|
||||
import styles from './index.module.scss';
|
||||
|
||||
interface IProps {
|
||||
templateId: string;
|
||||
}
|
||||
|
||||
const { Text } = Typography;
|
||||
|
||||
export const TemplateEditor: React.FC<IProps> = ({ templateId }) => {
|
||||
const { user } = useUser();
|
||||
const { data, loading, error, updateTemplate, deleteTemplate } = useTemplate(templateId);
|
||||
|
||||
const { width: windowWidth } = useWindowSize();
|
||||
const [title, setTitle] = useState(data.title);
|
||||
const [isPublic, setPublic] = useState(false);
|
||||
const { width, fontSize } = useDocumentStyle();
|
||||
const editorWrapClassNames = useMemo(() => {
|
||||
return width === 'standardWidth' ? styles.isStandardWidth : styles.isFullWidth;
|
||||
}, [width]);
|
||||
|
||||
const goback = useCallback(() => {
|
||||
Router.back();
|
||||
}, []);
|
||||
|
||||
const handleDelte = useCallback(() => {
|
||||
deleteTemplate().then(() => {
|
||||
goback();
|
||||
});
|
||||
}, [deleteTemplate, goback]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!data) return;
|
||||
setPublic(data.isPublic);
|
||||
}, [data]);
|
||||
|
||||
return (
|
||||
<DataRender
|
||||
loading={loading}
|
||||
|
@ -27,9 +61,53 @@ export const TemplateEditor: React.FC<IProps> = ({ templateId }) => {
|
|||
return (
|
||||
<>
|
||||
<Seo title={data.title} />
|
||||
{user && data && (
|
||||
<Editor user={user} data={data} updateTemplate={updateTemplate} deleteTemplate={deleteTemplate} />
|
||||
)}
|
||||
<div className={styles.wrap}>
|
||||
<header>
|
||||
<Nav
|
||||
style={{ overflow: 'auto' }}
|
||||
mode="horizontal"
|
||||
header={
|
||||
<>
|
||||
<Tooltip content="返回" position="bottom">
|
||||
<Button onClick={goback} icon={<IconChevronLeft />} style={{ marginRight: 16 }} />
|
||||
</Tooltip>
|
||||
<Text strong ellipsis={{ showTooltip: true }} style={{ width: ~~(windowWidth / 4) }}>
|
||||
{title}
|
||||
</Text>
|
||||
</>
|
||||
}
|
||||
footer={
|
||||
<Space>
|
||||
<Popover key="style" zIndex={1061} position="bottomLeft" content={<DocumentStyle />}>
|
||||
<Button icon={<IconArticle />} theme="borderless" type="tertiary" />
|
||||
</Popover>
|
||||
<Tooltip position="bottom" content={isPublic ? '公开模板' : '个人模板'}>
|
||||
<Switch onChange={(v) => updateTemplate({ isPublic: v })}></Switch>
|
||||
</Tooltip>
|
||||
<Popconfirm title="删除模板" content="模板删除后不可恢复,谨慎操作!" onConfirm={handleDelte}>
|
||||
<Button type="danger">删除</Button>
|
||||
</Popconfirm>
|
||||
<Theme />
|
||||
<User />
|
||||
</Space>
|
||||
}
|
||||
></Nav>
|
||||
</header>
|
||||
<main className={styles.contentWrap}>
|
||||
<div className={styles.editorWrap}>
|
||||
<div className={cls(styles.contentWrap, editorWrapClassNames)} style={{ fontSize }}>
|
||||
<CollaborationEditor
|
||||
menubar
|
||||
editable
|
||||
user={user}
|
||||
id={data.id}
|
||||
type="template"
|
||||
onTitleUpdate={setTitle}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
|
|
|
@ -14,26 +14,27 @@ interface IProps {
|
|||
export const WikiOrDocumentCreator: React.FC<IProps> = ({ onCreateDocument, children }) => {
|
||||
const { isMobile } = useWindowSize();
|
||||
const { wikiId, docId } = useQuery<{ wikiId?: string; docId?: string }>();
|
||||
const [dropdownVisible, toggleDropdownVisible] = useToggle(false);
|
||||
const [visible, toggleVisible] = useToggle(false);
|
||||
const [createDocumentModalVisible, toggleCreateDocumentModalVisible] = useToggle(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Dropdown
|
||||
trigger="click"
|
||||
visible={dropdownVisible}
|
||||
onVisibleChange={toggleDropdownVisible}
|
||||
render={
|
||||
<Dropdown.Menu>
|
||||
// @ts-ignore
|
||||
<Dropdown.Menu onClick={toggleDropdownVisible}>
|
||||
<Dropdown.Item onClick={toggleVisible}>知识库</Dropdown.Item>
|
||||
{wikiId && <Dropdown.Item onClick={toggleCreateDocumentModalVisible}>文档</Dropdown.Item>}
|
||||
</Dropdown.Menu>
|
||||
}
|
||||
>
|
||||
{children || isMobile ? (
|
||||
<Button type="primary" theme="solid" icon={<IconPlus />} size="small" />
|
||||
) : (
|
||||
<Button type="primary" theme="solid" icon={<IconChevronDown />} iconPosition="right">
|
||||
新建
|
||||
</Button>
|
||||
)}
|
||||
<span onClick={toggleDropdownVisible}>
|
||||
{children || <Button type="primary" theme="solid" icon={<IconPlus />} size="small" />}
|
||||
</span>
|
||||
</Dropdown>
|
||||
<WikiCreator visible={visible} toggleVisible={toggleVisible} />
|
||||
{wikiId && (
|
||||
|
|
|
@ -67,6 +67,7 @@ const menus = [
|
|||
export const RouterHeader: React.FC = () => {
|
||||
const { pathname } = useRouter();
|
||||
const { width, isMobile } = useWindowSize();
|
||||
const [dropdownVisible, toggleDropdownVisible] = useToggle(false);
|
||||
const [recentModalVisible, toggleRecentModalVisible] = useToggle(false);
|
||||
const [wikiModalVisible, toggleWikiModalVisible] = useToggle(false);
|
||||
|
||||
|
@ -82,8 +83,11 @@ export const RouterHeader: React.FC = () => {
|
|||
<Dropdown
|
||||
trigger="click"
|
||||
position="bottomRight"
|
||||
visible={dropdownVisible}
|
||||
onVisibleChange={toggleDropdownVisible}
|
||||
render={
|
||||
<Dropdown.Menu>
|
||||
// @ts-ignore
|
||||
<Dropdown.Menu onClick={toggleDropdownVisible}>
|
||||
{menus.slice(0, 1).map((menu) => {
|
||||
return (
|
||||
<Dropdown.Item key={menu.itemKey} onClick={menu.onClick}>
|
||||
|
@ -103,7 +107,7 @@ export const RouterHeader: React.FC = () => {
|
|||
</Dropdown.Menu>
|
||||
}
|
||||
>
|
||||
<Button icon={<IconMenu />} type="tertiary" theme="borderless" />
|
||||
<Button icon={<IconMenu />} type="tertiary" theme="borderless" onMouseDown={toggleDropdownVisible} />
|
||||
</Dropdown>
|
||||
</Space>
|
||||
|
||||
|
|
|
@ -84,7 +84,9 @@ export const RecentModal = ({ visible, toggleVisible }) => {
|
|||
onCancel={toggleVisible}
|
||||
style={{ maxWidth: '96vw' }}
|
||||
>
|
||||
<RecentDocs />
|
||||
<div style={{ paddingBottom: 24 }}>
|
||||
<RecentDocs />
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -150,7 +150,9 @@ export const WikiModal = ({ visible, toggleVisible }) => {
|
|||
onCancel={toggleVisible}
|
||||
style={{ maxWidth: '96vw' }}
|
||||
>
|
||||
<WikiContent />
|
||||
<div style={{ paddingBottom: 24 }}>
|
||||
<WikiContent />
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -25,27 +25,6 @@ type IProps = Pick<
|
|||
status: ProviderStatus;
|
||||
};
|
||||
|
||||
function scrollEditor(editor) {
|
||||
try {
|
||||
/**
|
||||
* 修复移动端编辑问题
|
||||
*/
|
||||
setTimeout(() => {
|
||||
try {
|
||||
const element = editor.options.element;
|
||||
// 脏代码:这里使用 parentElement 是和布局有关的,需要根据实际情况修改
|
||||
const parentElement = element.parentNode as HTMLElement;
|
||||
const nextScrollTop = element.scrollHeight;
|
||||
parentElement.scrollTop = nextScrollTop;
|
||||
} catch (e) {
|
||||
//
|
||||
}
|
||||
}, 0);
|
||||
} catch (e) {
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
export const EditorInstance = forwardRef((props: IProps, ref) => {
|
||||
const { hocuspocusProvider, editable, user, onTitleUpdate, status, menubar, renderInEditorPortal } = props;
|
||||
const $headerContainer = useRef<HTMLDivElement>();
|
||||
|
@ -113,6 +92,8 @@ export const EditorInstance = forwardRef((props: IProps, ref) => {
|
|||
|
||||
// 监听键盘收起、打开
|
||||
useEffect(() => {
|
||||
if (!isMobile) return;
|
||||
|
||||
let cleanUp = () => {};
|
||||
const focusIn = () => {
|
||||
setTimeout(() => {
|
||||
|
@ -149,7 +130,7 @@ export const EditorInstance = forwardRef((props: IProps, ref) => {
|
|||
return () => {
|
||||
cleanUp();
|
||||
};
|
||||
}, []);
|
||||
}, [isMobile]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -159,22 +140,29 @@ export const EditorInstance = forwardRef((props: IProps, ref) => {
|
|||
description="我们已与您断开连接,您可以继续编辑文档。一旦重新连接,我们会自动重新提交数据。"
|
||||
/>
|
||||
)}
|
||||
{/* FIXME:需要菜单栏但是无法编辑,则认为进入了编辑模式但是没有编辑权限 */}
|
||||
|
||||
{/* FIXME:需要菜单栏但是无法编辑,则认为进入了编辑模式但是没有编辑权限,也许有更好的判断 */}
|
||||
{!editable && menubar && (
|
||||
<Banner type="warning" description="您没有编辑权限,暂不能编辑该文档。" closeable={false} />
|
||||
)}
|
||||
|
||||
{menubar && (
|
||||
<header className={cls(isMobile && styles.mobileToolbar)} ref={$headerContainer}>
|
||||
<MenuBar editor={editor} />
|
||||
</header>
|
||||
)}
|
||||
|
||||
<main ref={$mainContainer}>
|
||||
<EditorContent editor={editor} />
|
||||
{protals}
|
||||
</main>
|
||||
|
||||
{editable && menubar && (
|
||||
<BackTop target={() => $mainContainer.current} style={{ right: 16, bottom: 65 }} visibilityHeight={200} />
|
||||
<BackTop
|
||||
target={() => $mainContainer.current}
|
||||
style={{ right: isMobile ? 16 : 100, bottom: 65 }}
|
||||
visibilityHeight={200}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue