mirror of https://github.com/fantasticit/think.git
tiptap: improve document-reference menu
parent
9042da011f
commit
c9d4fd5127
|
@ -34,6 +34,7 @@ import { Search } from './menus/search';
|
|||
|
||||
import { Banner } from './menus/banner';
|
||||
import { Countdonw } from './menus/countdown';
|
||||
import { DocumentReference } from './menus/document-reference';
|
||||
import { Image } from './menus/image';
|
||||
import { Iframe } from './menus/iframe';
|
||||
import { Table } from './menus/table';
|
||||
|
@ -89,6 +90,7 @@ export const MenuBar: React.FC<{ editor: any }> = ({ editor }) => {
|
|||
|
||||
<Banner editor={editor} />
|
||||
<Countdonw editor={editor} />
|
||||
<DocumentReference editor={editor} />
|
||||
<Image editor={editor} />
|
||||
<Iframe editor={editor} />
|
||||
<Table editor={editor} />
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
import { useCallback } from 'react';
|
||||
import { useRouter } from 'next/router';
|
||||
import { Space, Button, List, Popover } from '@douyinfe/semi-ui';
|
||||
import { IconEdit, IconDelete } from '@douyinfe/semi-icons';
|
||||
import { Tooltip } from 'components/tooltip';
|
||||
import { DataRender } from 'components/data-render';
|
||||
import { useWikiTocs } from 'data/wiki';
|
||||
import { BubbleMenu } from '../../views/bubble-menu';
|
||||
import { DocumentReference } from '../../extensions/document-reference';
|
||||
import { Divider } from '../../divider';
|
||||
|
||||
export const DocumentReferenceBubbleMenu = ({ editor }) => {
|
||||
const { pathname, query } = useRouter();
|
||||
const wikiIdFromUrl = query?.wikiId;
|
||||
const isShare = pathname.includes('share');
|
||||
const { data: tocs, loading, error } = useWikiTocs(isShare ? null : wikiIdFromUrl);
|
||||
|
||||
const selectDoc = useCallback(
|
||||
(item) => {
|
||||
const { wikiId, title, id: documentId } = item;
|
||||
|
||||
editor
|
||||
.chain()
|
||||
.updateAttributes(DocumentReference.name, { wikiId, documentId, title })
|
||||
.setNodeSelection(editor.state.selection.from)
|
||||
.focus()
|
||||
.run();
|
||||
},
|
||||
[editor]
|
||||
);
|
||||
|
||||
const deleteNode = useCallback(() => editor.chain().deleteSelection().run(), [editor]);
|
||||
|
||||
return (
|
||||
<BubbleMenu
|
||||
className={'bubble-menu'}
|
||||
editor={editor}
|
||||
pluginKey="countdonw-bubble-menu"
|
||||
shouldShow={() => editor.isActive(DocumentReference.name)}
|
||||
tippyOptions={{ maxWidth: 456 }}
|
||||
>
|
||||
<Space>
|
||||
<Popover
|
||||
spacing={10}
|
||||
content={
|
||||
<DataRender
|
||||
loading={loading}
|
||||
error={error}
|
||||
normalContent={() => (
|
||||
<List
|
||||
style={{ maxHeight: 320, overflow: 'auto' }}
|
||||
dataSource={tocs}
|
||||
renderItem={(item) => (
|
||||
<List.Item
|
||||
onClick={() => selectDoc(item)}
|
||||
style={{ cursor: 'pointer' }}
|
||||
main={<span style={{ color: 'var(--semi-color-text-0)', fontWeight: 500 }}>{item.title}</span>}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
}
|
||||
trigger="click"
|
||||
>
|
||||
<Button size="small" type="tertiary" theme="borderless" icon={<IconEdit />} />
|
||||
</Popover>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Tooltip content="删除节点" hideOnClick>
|
||||
<Button onClick={deleteNode} icon={<IconDelete />} type="tertiary" theme="borderless" size="small" />
|
||||
</Tooltip>
|
||||
</Space>
|
||||
</BubbleMenu>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,15 @@
|
|||
import React from 'react';
|
||||
import { Editor } from '@tiptap/core';
|
||||
import { DocumentReferenceBubbleMenu } from './bubble';
|
||||
|
||||
export const DocumentReference: React.FC<{ editor: Editor }> = ({ editor }) => {
|
||||
if (!editor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<DocumentReferenceBubbleMenu editor={editor} />
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -1,5 +1,5 @@
|
|||
.wrap {
|
||||
margin-top: .75em;
|
||||
margin-top: 0.75em;
|
||||
border-radius: var(--border-radius);
|
||||
|
||||
.itemWrap {
|
||||
|
@ -7,7 +7,6 @@
|
|||
padding: 8px;
|
||||
color: var(--node-text-color);
|
||||
text-decoration: none;
|
||||
border: 1px solid var(--node-border-color);
|
||||
border-radius: var(--border-radius);
|
||||
align-items: center;
|
||||
|
||||
|
@ -27,21 +26,7 @@
|
|||
color: var(--node-text-color);
|
||||
text-decoration: none;
|
||||
cursor: not-allowed;
|
||||
border: 1px solid var(--node-border-color);
|
||||
border-radius: var(--border-radius);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&.isEditable {
|
||||
padding: 12px;
|
||||
|
||||
.itemWrap {
|
||||
margin-top: 12px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.empty {
|
||||
margin-top: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,56 +2,35 @@ import { NodeViewWrapper } from '@tiptap/react';
|
|||
import { useRouter } from 'next/router';
|
||||
import Link from 'next/link';
|
||||
import cls from 'classnames';
|
||||
import { Select } from '@douyinfe/semi-ui';
|
||||
import { useWikiTocs } from 'data/wiki';
|
||||
import { DataRender } from 'components/data-render';
|
||||
import { IconDocument } from 'components/icons';
|
||||
import styles from './index.module.scss';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export const DocumentReferenceWrapper = ({ editor, node, updateAttributes }) => {
|
||||
const { pathname, query } = useRouter();
|
||||
const wikiIdFromUrl = query?.wikiId;
|
||||
const { pathname } = useRouter();
|
||||
const isShare = pathname.includes('share');
|
||||
const isEditable = editor.isEditable;
|
||||
const { wikiId, documentId, title } = node.attrs;
|
||||
const { data: tocs, loading, error } = useWikiTocs(isShare ? null : wikiIdFromUrl);
|
||||
|
||||
const selectDoc = (str) => {
|
||||
const [wikiId, title, documentId] = str.split('/');
|
||||
updateAttributes({ wikiId, documentId, title });
|
||||
};
|
||||
const content = useMemo(() => {
|
||||
if (!wikiId && !documentId) {
|
||||
return (
|
||||
<div className={cls(styles.empty, !isEditable && 'render-wrapper')}>
|
||||
<span>{'用户未选择文档'}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (isEditable) {
|
||||
return (
|
||||
<div className={cls(styles.itemWrap)}>
|
||||
<IconDocument />
|
||||
<span>{title}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<NodeViewWrapper
|
||||
as="div"
|
||||
className={cls(styles.wrap, isEditable && styles.isEditable, isEditable && 'render-wrapper')}
|
||||
>
|
||||
<div>
|
||||
{isEditable && (
|
||||
<DataRender
|
||||
loading={loading}
|
||||
error={error}
|
||||
normalContent={() => (
|
||||
<Select
|
||||
placeholder="请选择文档"
|
||||
onChange={(v) => selectDoc(v)}
|
||||
style={{ maxWidth: 180 }}
|
||||
{...(wikiId && documentId ? { value: `${wikiId}/${title}/${documentId}` } : {})}
|
||||
>
|
||||
{(tocs || []).map((toc) => (
|
||||
<Select.Option
|
||||
// FIXME: semi-design 抄 antd,抄的什么玩意!!!
|
||||
label={`${toc.title}/${toc.id}`}
|
||||
value={`${toc.wikiId}/${toc.title}/${toc.id}`}
|
||||
>
|
||||
{toc.title}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
{wikiId && documentId ? (
|
||||
<Link
|
||||
key={documentId}
|
||||
href={{
|
||||
|
@ -64,12 +43,12 @@ export const DocumentReferenceWrapper = ({ editor, node, updateAttributes }) =>
|
|||
<span>{title || '请选择文档'}</span>
|
||||
</a>
|
||||
</Link>
|
||||
) : (
|
||||
<div className={cls(styles.empty, !isEditable && 'render-wrapper')}>
|
||||
<span>{'用户未选择文档'}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}, [wikiId, documentId]);
|
||||
|
||||
return (
|
||||
<NodeViewWrapper as="div" className={cls(styles.wrap, isEditable && 'render-wrapper')}>
|
||||
{content}
|
||||
</NodeViewWrapper>
|
||||
);
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue