mirror of https://github.com/fantasticit/think.git
client: use dropdown instad of popover
parent
ef8d3924b9
commit
232c818c81
|
@ -4,6 +4,7 @@ import {
|
|||
AvatarGroup,
|
||||
Button,
|
||||
Checkbox,
|
||||
Dropdown,
|
||||
Input,
|
||||
Modal,
|
||||
Popconfirm,
|
||||
|
@ -24,7 +25,7 @@ import { useUser } from 'data/user';
|
|||
import { event, JOIN_USER } from 'event';
|
||||
import { IsOnMobile } from 'hooks/use-on-mobile';
|
||||
import { useToggle } from 'hooks/use-toggle';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
|
||||
interface IProps {
|
||||
wikiId: string;
|
||||
|
@ -79,6 +80,71 @@ export const DocumentCollaboration: React.FC<IProps> = ({ wikiId, documentId, di
|
|||
[deleteUser]
|
||||
);
|
||||
|
||||
const content = useMemo(
|
||||
() => (
|
||||
<Tabs type="line">
|
||||
<TabPane tab="添加成员" itemKey="add">
|
||||
<div style={{ marginTop: 16 }}>
|
||||
<Input ref={ref} placeholder="输入对方用户名" value={inviteUser} onChange={setInviteUser}></Input>
|
||||
<Paragraph style={{ marginTop: 16 }}>
|
||||
邀请成功后,请将该链接发送给对方。
|
||||
<span style={{ verticalAlign: 'middle' }}>
|
||||
<DocumentLinkCopyer wikiId={wikiId} documentId={documentId} />
|
||||
</span>
|
||||
</Paragraph>
|
||||
<Button theme="solid" block style={{ margin: '24px 0' }} disabled={!inviteUser} onClick={handleOk}>
|
||||
添加用户
|
||||
</Button>
|
||||
</div>
|
||||
</TabPane>
|
||||
<TabPane tab="协作成员" itemKey="list">
|
||||
<DataRender
|
||||
loading={loading}
|
||||
error={error}
|
||||
loadingContent={<Spin />}
|
||||
normalContent={() => (
|
||||
<Table dataSource={users} size="small" pagination>
|
||||
<Column title="用户名" dataIndex="user.name" key="name" />
|
||||
<Column
|
||||
title="是否可读"
|
||||
dataIndex="auth.readable"
|
||||
key="readable"
|
||||
render={renderChecked(updateUser, 'readable')}
|
||||
align="center"
|
||||
/>
|
||||
<Column
|
||||
title="是否可编辑"
|
||||
dataIndex="auth.editable"
|
||||
key="editable"
|
||||
render={renderChecked(updateUser, 'editable')}
|
||||
align="center"
|
||||
/>
|
||||
<Column
|
||||
title="操作"
|
||||
dataIndex="operate"
|
||||
key="operate"
|
||||
render={(_, document) => (
|
||||
<Popconfirm showArrow title="确认删除该成员?" onConfirm={() => handleDelete(document)}>
|
||||
<Button type="tertiary" theme="borderless" icon={<IconDelete />} />
|
||||
</Popconfirm>
|
||||
)}
|
||||
/>
|
||||
</Table>
|
||||
)}
|
||||
/>
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
),
|
||||
[documentId, error, handleDelete, handleOk, inviteUser, loading, updateUser, users, wikiId]
|
||||
);
|
||||
|
||||
const btn = useMemo(
|
||||
() => (
|
||||
<Button theme="borderless" type="tertiary" disabled={disabled} icon={<IconUserAdd />} onClick={toggleVisible} />
|
||||
),
|
||||
[disabled, toggleVisible]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (visible) {
|
||||
setTimeout(() => ref.current?.focus(), 100);
|
||||
|
@ -139,70 +205,41 @@ export const DocumentCollaboration: React.FC<IProps> = ({ wikiId, documentId, di
|
|||
);
|
||||
})}
|
||||
</AvatarGroup>
|
||||
<Popover
|
||||
showArrow
|
||||
visible={visible}
|
||||
onVisibleChange={toggleVisible}
|
||||
trigger="click"
|
||||
position={isMobile ? 'topRight' : 'bottomLeft'}
|
||||
style={{ width: 376, maxWidth: '80vw' }}
|
||||
content={
|
||||
<Tabs type="line">
|
||||
<TabPane tab="添加成员" itemKey="add">
|
||||
<div style={{ marginTop: 16 }}>
|
||||
<Input ref={ref} placeholder="输入对方用户名" value={inviteUser} onChange={setInviteUser}></Input>
|
||||
<Paragraph style={{ marginTop: 16 }}>
|
||||
邀请成功后,请将该链接发送给对方。
|
||||
<span style={{ verticalAlign: 'middle' }}>
|
||||
<DocumentLinkCopyer wikiId={wikiId} documentId={documentId} />
|
||||
</span>
|
||||
</Paragraph>
|
||||
<Button theme="solid" block style={{ margin: '24px 0' }} disabled={!inviteUser} onClick={handleOk}>
|
||||
添加用户
|
||||
</Button>
|
||||
</div>
|
||||
</TabPane>
|
||||
<TabPane tab="协作成员" itemKey="list">
|
||||
<DataRender
|
||||
loading={loading}
|
||||
error={error}
|
||||
loadingContent={<Spin />}
|
||||
normalContent={() => (
|
||||
<Table dataSource={users} size="small" pagination>
|
||||
<Column title="用户名" dataIndex="user.name" key="name" />
|
||||
<Column
|
||||
title="是否可读"
|
||||
dataIndex="auth.readable"
|
||||
key="readable"
|
||||
render={renderChecked(updateUser, 'readable')}
|
||||
align="center"
|
||||
/>
|
||||
<Column
|
||||
title="是否可编辑"
|
||||
dataIndex="auth.editable"
|
||||
key="editable"
|
||||
render={renderChecked(updateUser, 'editable')}
|
||||
align="center"
|
||||
/>
|
||||
<Column
|
||||
title="操作"
|
||||
dataIndex="operate"
|
||||
key="operate"
|
||||
render={(_, document) => (
|
||||
<Popconfirm showArrow title="确认删除该成员?" onConfirm={() => handleDelete(document)}>
|
||||
<Button type="tertiary" theme="borderless" icon={<IconDelete />} />
|
||||
</Popconfirm>
|
||||
)}
|
||||
/>
|
||||
</Table>
|
||||
)}
|
||||
/>
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
}
|
||||
>
|
||||
<Button theme="borderless" type="tertiary" disabled={disabled} icon={<IconUserAdd />} />
|
||||
</Popover>
|
||||
{isMobile ? (
|
||||
<>
|
||||
<Modal
|
||||
centered
|
||||
title="文档协作"
|
||||
visible={visible}
|
||||
footer={null}
|
||||
onCancel={toggleVisible}
|
||||
style={{ maxWidth: '96vw' }}
|
||||
>
|
||||
{content}
|
||||
</Modal>
|
||||
{btn}
|
||||
</>
|
||||
) : (
|
||||
<Dropdown
|
||||
visible={visible}
|
||||
onVisibleChange={toggleVisible}
|
||||
trigger="click"
|
||||
position="bottomRight"
|
||||
content={
|
||||
<div
|
||||
style={{
|
||||
width: 412,
|
||||
maxWidth: '96vw',
|
||||
padding: '0 24px',
|
||||
}}
|
||||
>
|
||||
{content}
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{btn}
|
||||
</Dropdown>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { IconLink } from '@douyinfe/semi-icons';
|
||||
import { Button, Input, Popover, Space, Toast, Typography } from '@douyinfe/semi-ui';
|
||||
import { Button, Dropdown, Input, Modal, Space, Toast, Typography } from '@douyinfe/semi-ui';
|
||||
import { isPublicDocument } from '@think/domains';
|
||||
import { useDocumentDetail } from 'data/document';
|
||||
import { getDocumentShareURL } from 'helpers/url';
|
||||
|
@ -28,6 +28,7 @@ export const DocumentShare: React.FC<IProps> = ({ documentId, disabled = false,
|
|||
const copyable = useMemo(
|
||||
() => ({
|
||||
onCopy: () => Toast.success({ content: '复制文本成功' }),
|
||||
successTip: '已复制',
|
||||
}),
|
||||
[]
|
||||
);
|
||||
|
@ -44,6 +45,73 @@ export const DocumentShare: React.FC<IProps> = ({ documentId, disabled = false,
|
|||
toggleStatus({ sharePassword: isPublic ? '' : sharePassword });
|
||||
}, [isPublic, sharePassword, toggleStatus]);
|
||||
|
||||
const content = useMemo(
|
||||
() => (
|
||||
<div
|
||||
style={{
|
||||
maxWidth: '96vw',
|
||||
overflow: 'auto',
|
||||
}}
|
||||
onClick={prevent}
|
||||
>
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
<ShareIllustration />
|
||||
</div>
|
||||
{isPublic ? (
|
||||
<Text
|
||||
ellipsis
|
||||
icon={<IconLink />}
|
||||
copyable={copyable}
|
||||
style={{
|
||||
width: 280,
|
||||
}}
|
||||
>
|
||||
{shareUrl}
|
||||
</Text>
|
||||
) : (
|
||||
<Input
|
||||
ref={ref}
|
||||
mode="password"
|
||||
placeholder="设置访问密码"
|
||||
value={sharePassword}
|
||||
onChange={setSharePassword}
|
||||
></Input>
|
||||
)}
|
||||
<div style={{ marginTop: 16 }}>
|
||||
<Text type="tertiary">
|
||||
{isPublic
|
||||
? '分享开启后,该页面包含的所有内容均可访问,请谨慎开启'
|
||||
: ' 分享关闭后,非协作成员将不能继续访问该页面'}
|
||||
</Text>
|
||||
</div>
|
||||
<Space style={{ width: '100%', justifyContent: 'end', margin: '12px 0' }}>
|
||||
<Button onClick={() => toggleVisible(false)}>取消</Button>
|
||||
<Button theme="solid" type={isPublic ? 'danger' : 'primary'} onClick={handleOk}>
|
||||
{isPublic ? '关闭分享' : '开启分享'}
|
||||
</Button>
|
||||
{isPublic && (
|
||||
<Button theme="solid" type="primary" onClick={viewUrl}>
|
||||
查看文档
|
||||
</Button>
|
||||
)}
|
||||
</Space>
|
||||
</div>
|
||||
),
|
||||
[copyable, handleOk, isPublic, prevent, sharePassword, shareUrl, toggleVisible, viewUrl]
|
||||
);
|
||||
|
||||
const btn = useMemo(
|
||||
() =>
|
||||
render ? (
|
||||
render({ isPublic, disabled, toggleVisible })
|
||||
) : (
|
||||
<Button disabled={disabled} type="primary" theme="light" onClick={toggleVisible}>
|
||||
{isPublic ? '分享中' : '分享'}
|
||||
</Button>
|
||||
),
|
||||
[disabled, isPublic, render, toggleVisible]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (loading || !data) return;
|
||||
setSharePassword(data.document && data.document.sharePassword);
|
||||
|
@ -56,72 +124,42 @@ export const DocumentShare: React.FC<IProps> = ({ documentId, disabled = false,
|
|||
}, [visible]);
|
||||
|
||||
return (
|
||||
<Popover
|
||||
showArrow
|
||||
visible={visible}
|
||||
onVisibleChange={toggleVisible}
|
||||
trigger="click"
|
||||
position={isMobile ? 'top' : 'bottomLeft'}
|
||||
style={{ width: 376, maxWidth: '80vw' }}
|
||||
content={
|
||||
<div
|
||||
style={{
|
||||
maxHeight: '70vh',
|
||||
overflow: 'auto',
|
||||
}}
|
||||
onClick={prevent}
|
||||
>
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
<ShareIllustration />
|
||||
</div>
|
||||
{isPublic ? (
|
||||
<Text
|
||||
ellipsis
|
||||
icon={<IconLink />}
|
||||
copyable={copyable}
|
||||
<>
|
||||
{isMobile ? (
|
||||
<>
|
||||
<Modal
|
||||
centered
|
||||
title="文档分享"
|
||||
visible={visible}
|
||||
footer={null}
|
||||
onCancel={toggleVisible}
|
||||
style={{ maxWidth: '96vw' }}
|
||||
>
|
||||
{content}
|
||||
</Modal>
|
||||
{btn}
|
||||
</>
|
||||
) : (
|
||||
<Dropdown
|
||||
visible={visible}
|
||||
onVisibleChange={toggleVisible}
|
||||
trigger="click"
|
||||
position="bottomRight"
|
||||
content={
|
||||
<div
|
||||
style={{
|
||||
width: 240,
|
||||
width: 412,
|
||||
maxWidth: '96vw',
|
||||
padding: '0 24px',
|
||||
}}
|
||||
>
|
||||
{shareUrl}
|
||||
</Text>
|
||||
) : (
|
||||
<Input
|
||||
ref={ref}
|
||||
mode="password"
|
||||
placeholder="设置访问密码"
|
||||
value={sharePassword}
|
||||
onChange={setSharePassword}
|
||||
></Input>
|
||||
)}
|
||||
<div style={{ marginTop: 16 }}>
|
||||
<Text type="tertiary">
|
||||
{isPublic
|
||||
? '分享开启后,该页面包含的所有内容均可访问,请谨慎开启'
|
||||
: ' 分享关闭后,其他人将不能继续访问该页面'}
|
||||
</Text>
|
||||
</div>
|
||||
<Space style={{ width: '100%', justifyContent: 'end', margin: '12px 0' }}>
|
||||
<Button onClick={() => toggleVisible(false)}>取消</Button>
|
||||
<Button theme="solid" type={isPublic ? 'danger' : 'primary'} onClick={handleOk}>
|
||||
{isPublic ? '关闭分享' : '开启分享'}
|
||||
</Button>
|
||||
{isPublic && (
|
||||
<Button theme="solid" type="primary" onClick={viewUrl}>
|
||||
查看文档
|
||||
</Button>
|
||||
)}
|
||||
</Space>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{render ? (
|
||||
render({ isPublic, disabled, toggleVisible })
|
||||
) : (
|
||||
<Button disabled={disabled} type="primary" theme="light" onClick={toggleVisible}>
|
||||
{isPublic ? '分享中' : '分享'}
|
||||
</Button>
|
||||
{content}
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{btn}
|
||||
</Dropdown>
|
||||
)}
|
||||
</Popover>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { IconArticle } from '@douyinfe/semi-icons';
|
||||
import { Button, Popover, Radio, RadioGroup, Slider, Typography } from '@douyinfe/semi-ui';
|
||||
import { Button, Dropdown, Radio, RadioGroup, Slider, Typography } from '@douyinfe/semi-ui';
|
||||
import { throttle } from 'helpers/throttle';
|
||||
import { useDocumentStyle } from 'hooks/use-document-style';
|
||||
import { IsOnMobile } from 'hooks/use-on-mobile';
|
||||
|
@ -21,15 +21,14 @@ export const DocumentStyle = () => {
|
|||
}, [setWidth]);
|
||||
|
||||
return (
|
||||
<Popover
|
||||
<Dropdown
|
||||
key="style"
|
||||
showArrow
|
||||
trigger="click"
|
||||
zIndex={1061}
|
||||
position={isMobile ? 'topRight' : 'bottomLeft'}
|
||||
visible={visible}
|
||||
onVisibleChange={toggleVisible}
|
||||
style={{ padding: 0 }}
|
||||
onClickOutSide={toggleVisible}
|
||||
content={
|
||||
<div className={styles.wrap}>
|
||||
<div className={styles.item}>
|
||||
|
@ -50,6 +49,6 @@ export const DocumentStyle = () => {
|
|||
}
|
||||
>
|
||||
<Button icon={<IconArticle />} theme="borderless" type="tertiary" onMouseDown={toggleVisible} />
|
||||
</Popover>
|
||||
</Dropdown>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Badge, Button, Dropdown, Modal, Pagination, Popover, TabPane, Tabs, Typography } from '@douyinfe/semi-ui';
|
||||
import { Badge, Button, Dropdown, Modal, Pagination, TabPane, Tabs, Typography } from '@douyinfe/semi-ui';
|
||||
import { DataRender } from 'components/data-render';
|
||||
import { Empty } from 'components/empty';
|
||||
import { IconMessage } from 'components/icons/IconMessage';
|
||||
|
@ -196,15 +196,13 @@ const MessageBox = () => {
|
|||
{btn}
|
||||
</>
|
||||
) : (
|
||||
<Popover
|
||||
showArrow
|
||||
style={{ padding: 0 }}
|
||||
<Dropdown
|
||||
position="bottomRight"
|
||||
trigger="click"
|
||||
content={<div style={{ width: 300, padding: '16px 16px 0' }}>{content}</div>}
|
||||
>
|
||||
{btn}
|
||||
</Popover>
|
||||
</Dropdown>
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { IconSearch as SemiIconSearch } from '@douyinfe/semi-icons';
|
||||
import { Button, Input, Modal, Spin, Typography } from '@douyinfe/semi-ui';
|
||||
import { Button, Dropdown, Input, Modal, Spin, Typography } from '@douyinfe/semi-ui';
|
||||
import { IDocument } from '@think/domains';
|
||||
import { DataRender } from 'components/data-render';
|
||||
import { DocumentStar } from 'components/document/star';
|
||||
|
@ -8,10 +8,11 @@ import { IconSearch } from 'components/icons';
|
|||
import { IconDocumentFill } from 'components/icons/IconDocumentFill';
|
||||
import { LocaleTime } from 'components/locale-time';
|
||||
import { useAsyncLoading } from 'hooks/use-async-loading';
|
||||
import { IsOnMobile } from 'hooks/use-on-mobile';
|
||||
import { useToggle } from 'hooks/use-toggle';
|
||||
import Link from 'next/link';
|
||||
import Router from 'next/router';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { HttpClient } from 'services/http-client';
|
||||
|
||||
import styles from './index.module.scss';
|
||||
|
@ -68,6 +69,8 @@ const List: React.FC<{ data: IDocument[] }> = ({ data }) => {
|
|||
};
|
||||
|
||||
export const Search = () => {
|
||||
const ref = useRef<HTMLInputElement>();
|
||||
const { isMobile } = IsOnMobile.useHook();
|
||||
const [visible, toggleVisible] = useToggle(false);
|
||||
const [searchApi, loading] = useAsyncLoading(searchDocument, 10);
|
||||
const [keyword, setKeyword] = useState('');
|
||||
|
@ -85,6 +88,60 @@ export const Search = () => {
|
|||
});
|
||||
}, [searchApi, keyword]);
|
||||
|
||||
const onKeywordChange = useCallback((val) => {
|
||||
setSearchDocs([]);
|
||||
setKeyword(val);
|
||||
}, []);
|
||||
|
||||
const content = useMemo(
|
||||
() => (
|
||||
<div style={{ paddingBottom: 24 }}>
|
||||
<div>
|
||||
<Input
|
||||
showClear
|
||||
ref={ref}
|
||||
placeholder={'搜索文档'}
|
||||
value={keyword}
|
||||
onChange={onKeywordChange}
|
||||
onEnterPress={search}
|
||||
suffix={<SemiIconSearch onClick={search} style={{ cursor: 'pointer' }} />}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ height: 'calc(68vh - 40px)', paddingBottom: 36, overflow: 'auto' }}>
|
||||
<DataRender
|
||||
loading={loading}
|
||||
loadingContent={
|
||||
<div
|
||||
style={{
|
||||
paddingTop: 30,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<Spin />
|
||||
</div>
|
||||
}
|
||||
error={error}
|
||||
normalContent={() => <List data={searchDocs} />}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
[error, keyword, loading, onKeywordChange, search, searchDocs]
|
||||
);
|
||||
|
||||
const btn = useMemo(
|
||||
() => <Button type="tertiary" theme="borderless" icon={<IconSearch />} onClick={toggleVisible} />,
|
||||
[toggleVisible]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (visible) {
|
||||
setTimeout(() => ref.current?.focus(), 100);
|
||||
}
|
||||
}, [visible]);
|
||||
|
||||
useEffect(() => {
|
||||
const fn = () => {
|
||||
toggleVisible(false);
|
||||
|
@ -99,56 +156,39 @@ export const Search = () => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<Button type="tertiary" theme="borderless" icon={<IconSearch />} onClick={toggleVisible} />
|
||||
<Modal
|
||||
visible={visible}
|
||||
title="文档搜索"
|
||||
footer={null}
|
||||
onCancel={toggleVisible}
|
||||
style={{
|
||||
maxWidth: '96vw',
|
||||
}}
|
||||
bodyStyle={{
|
||||
height: '68vh',
|
||||
}}
|
||||
>
|
||||
<div style={{ paddingBottom: 24 }}>
|
||||
<div>
|
||||
<Input
|
||||
autofocus
|
||||
placeholder={'搜索文档'}
|
||||
size="large"
|
||||
value={keyword}
|
||||
onChange={(val) => {
|
||||
setSearchDocs([]);
|
||||
setKeyword(val);
|
||||
}}
|
||||
onEnterPress={search}
|
||||
suffix={<SemiIconSearch onClick={search} style={{ cursor: 'pointer' }} />}
|
||||
showClear
|
||||
/>
|
||||
</div>
|
||||
<div style={{ height: 'calc(68vh - 40px)', paddingBottom: 36, overflow: 'auto' }}>
|
||||
<DataRender
|
||||
loading={loading}
|
||||
loadingContent={
|
||||
<div
|
||||
style={{
|
||||
paddingTop: 30,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<Spin />
|
||||
</div>
|
||||
}
|
||||
error={error}
|
||||
normalContent={() => <List data={searchDocs} />}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
{!isMobile ? (
|
||||
<Dropdown
|
||||
position="bottomRight"
|
||||
trigger="click"
|
||||
visible={visible}
|
||||
onVisibleChange={toggleVisible}
|
||||
content={
|
||||
<div style={{ width: 360, maxWidth: '96vw', maxHeight: '70vh', overflow: 'auto', padding: '16px 16px 0' }}>
|
||||
{content}
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{btn}
|
||||
</Dropdown>
|
||||
) : (
|
||||
<>
|
||||
<Modal
|
||||
visible={visible}
|
||||
title="文档搜索"
|
||||
footer={null}
|
||||
onCancel={toggleVisible}
|
||||
style={{
|
||||
maxWidth: '96vw',
|
||||
}}
|
||||
bodyStyle={{
|
||||
height: '68vh',
|
||||
}}
|
||||
>
|
||||
{content}
|
||||
</Modal>
|
||||
{btn}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -23,6 +23,12 @@ html:focus-within {
|
|||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion) {
|
||||
html:focus-within {
|
||||
scroll-behavior: auto;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
|
|
|
@ -194,9 +194,11 @@ export const EditorInstance = forwardRef((props: IProps, ref) => {
|
|||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className={styles.tocsWrap}>
|
||||
<Tocs editor={editor} getContainer={getTocsContainer} />
|
||||
</div>
|
||||
{!isMobile && (
|
||||
<div className={styles.tocsWrap}>
|
||||
<Tocs editor={editor} getContainer={getTocsContainer} />
|
||||
</div>
|
||||
)}
|
||||
{protals}
|
||||
{!editable && <ImageViewer container={$mainContainer.current} />}
|
||||
</main>
|
||||
|
|
|
@ -36,9 +36,11 @@ export const ReaderEditor: React.FC<IProps> = ({ content }) => {
|
|||
<div className={styles.contentWrap}>
|
||||
<EditorContent editor={editor} />
|
||||
</div>
|
||||
<div className={styles.tocsWrap}>
|
||||
<Tocs editor={editor} getContainer={getTocsContainer} />
|
||||
</div>
|
||||
{!isMobile && (
|
||||
<div className={styles.tocsWrap}>
|
||||
<Tocs editor={editor} getContainer={getTocsContainer} />
|
||||
</div>
|
||||
)}
|
||||
<ImageViewer container={$mainContainer.current} />
|
||||
</main>
|
||||
<BackTop
|
||||
|
|
|
@ -4,8 +4,8 @@ import { throttle } from 'helpers/throttle';
|
|||
import { useToggle } from 'hooks/use-toggle';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import scrollIntoView from 'scroll-into-view-if-needed';
|
||||
import { Editor } from 'tiptap/core';
|
||||
import { TableOfContents } from 'tiptap/core/extensions/table-of-contents';
|
||||
import { Editor } from 'tiptap/editor/react';
|
||||
import { findNode } from 'tiptap/prose-utils';
|
||||
|
||||
import styles from './index.module.scss';
|
||||
|
|
Loading…
Reference in New Issue