mirror of https://github.com/fantasticit/think.git
refactor: add eslint
parent
881b1c6846
commit
8a1da277f5
|
@ -2,5 +2,5 @@ node_modules
|
|||
**/.next/**
|
||||
**/_next/**
|
||||
**/dist/**
|
||||
./packages/client/src/tiptap/next.config.js
|
||||
.eslintrc.js
|
||||
./packages/client/src/tiptap/wrappers/mind/mind-elixir/iconfont/iconfont.js
|
||||
|
|
|
@ -1,17 +1,15 @@
|
|||
module.exports = {
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
ecmaVersion: 8,
|
||||
sourceType: 'module',
|
||||
ecmaFeatures: {
|
||||
impliedStrict: true,
|
||||
experimentalObjectRestSpread: true,
|
||||
},
|
||||
allowImportExportEverywhere: true,
|
||||
project: ['./packages/client/tsconfig.json'],
|
||||
},
|
||||
plugins: ['@typescript-eslint', 'react-hooks'],
|
||||
extends: ['eslint:recommended', 'plugin:react/recommended', 'plugin:@typescript-eslint/recommended', 'prettier'],
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.ts', '*.tsx', '.js', '.jsx'],
|
||||
parserOptions: {
|
||||
project: ['./packages/client/tsconfig.json'],
|
||||
},
|
||||
},
|
||||
],
|
||||
settings: {
|
||||
react: {
|
||||
version: 'detect',
|
||||
|
|
17
package.json
17
package.json
|
@ -4,7 +4,7 @@
|
|||
"author": "fantasticit",
|
||||
"scripts": {
|
||||
"clean": "npx rimraf ./node_modules ./packages/**/node_modules",
|
||||
"dev": "concurrently \"pnpm:dev:*\"",
|
||||
"dev": "concurrently 'pnpm:dev:*'",
|
||||
"dev:server": "pnpm run --dir packages/server dev",
|
||||
"dev:client": "pnpm run --dir packages/client dev",
|
||||
"build": "pnpm build:server && pnpm build:client",
|
||||
|
@ -14,17 +14,18 @@
|
|||
"build:config": "pnpm run --dir packages/config build",
|
||||
"build:server": "pnpm run --dir packages/server build",
|
||||
"build:client": "pnpm run --dir packages/client build",
|
||||
"start": "concurrently \"pnpm:start:*\"",
|
||||
"start": "concurrently 'pnpm:start:*'",
|
||||
"start:server": "pnpm run --dir packages/server start",
|
||||
"start:client": "pnpm run --dir packages/client start",
|
||||
"pm2": "pnpm run pm2:server && pnpm run pm2:client",
|
||||
"pm2:server": "pnpm run --dir packages/server pm2",
|
||||
"pm2:client": "pnpm run --dir packages/client pm2",
|
||||
"lint": "concurrently 'pnpm:lint:*'",
|
||||
"lint:client": "eslint --fix './packages/client/**/*.{ts,tsx,js,jsx}' -c '.eslintrc.client.js'",
|
||||
"lint:server": "eslint --fix './packages/server/src/*.{ts,js}' -c '.eslintrc.server.js'",
|
||||
"format": "concurrently \"pnpm:format:*\"",
|
||||
"format:ts": "prettier --write --parser typescript \"packages/**/*.{ts,tsx,js,jsx}\"",
|
||||
"format:css": "stylelint --fix --formatter verbose --allow-empty-input \"packages/**/*.{css,scss,sass}\"",
|
||||
"format": "concurrently 'pnpm:format:*'",
|
||||
"format:ts": "prettier --write --parser typescript 'packages/**/*.{ts,tsx,js,jsx}'",
|
||||
"format:css": "stylelint --fix --formatter verbose --allow-empty-input 'packages/**/*.{css,scss,sass}'",
|
||||
"prepare": "husky install",
|
||||
"precommit": "lint-staged"
|
||||
},
|
||||
|
@ -62,6 +63,12 @@
|
|||
},
|
||||
"lint-staged": {
|
||||
"*.{ts,tsx,js,jsx}": "prettier --write",
|
||||
"./packages/client/**/*.{ts,tsx,js,jsx}": [
|
||||
"eslint --fix -c '.eslintrc.client.js'"
|
||||
],
|
||||
"./packages/server/src/*.{ts,js}": [
|
||||
"eslint --fix -c '.eslintrc.server.js'"
|
||||
],
|
||||
"*.{css,scss,sass}": " stylelint --fix --formatter verbose --allow-empty-input"
|
||||
}
|
||||
}
|
|
@ -1,14 +1,16 @@
|
|||
/* eslint-disable */
|
||||
/* eslint-env es6 */
|
||||
const semi = require('@douyinfe/semi-next').default({});
|
||||
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
|
||||
const { getConfig } = require('@think/config');
|
||||
const config = getConfig();
|
||||
|
||||
const nextConfig = require('@douyinfe/semi-next').default({})({
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = semi({
|
||||
assetPrefix: config.assetPrefix,
|
||||
env: {
|
||||
SERVER_API_URL: config?.client?.apiUrl,
|
||||
COLLABORATION_API_URL: config?.client?.collaborationUrl,
|
||||
ENABLE_ALIYUN_OSS: !!config?.oss?.aliyun?.accessKeyId,
|
||||
SERVER_API_URL: config.client.apiUrl,
|
||||
COLLABORATION_API_URL: config.client.collaborationUrl,
|
||||
ENABLE_ALIYUN_OSS: !!config.oss.aliyun.accessKeyId,
|
||||
},
|
||||
webpack: (config, { dev, isServer }) => {
|
||||
config.resolve.plugins.push(new TsconfigPathsPlugin());
|
||||
|
|
|
@ -37,7 +37,6 @@ interface IProps {
|
|||
}
|
||||
|
||||
export const Editor: React.FC<IProps> = ({ user: currentUser, documentId, authority, className, style }) => {
|
||||
if (!currentUser) return null;
|
||||
const $hasShowUserSettingModal = useRef(false);
|
||||
const { users, addUser, updateUser } = useCollaborationDocument(documentId);
|
||||
const [status, setStatus] = useState<ProviderStatus>('connecting');
|
||||
|
@ -64,7 +63,7 @@ export const Editor: React.FC<IProps> = ({ user: currentUser, documentId, author
|
|||
},
|
||||
},
|
||||
});
|
||||
}, [documentId, currentUser.token]);
|
||||
}, [documentId, currentUser, toggleLoading]);
|
||||
const editor = useEditor({
|
||||
editable: authority && authority.editable,
|
||||
extensions: [
|
||||
|
@ -77,7 +76,9 @@ export const Editor: React.FC<IProps> = ({ user: currentUser, documentId, author
|
|||
try {
|
||||
const title = transaction.doc.content.firstChild.content.firstChild.textContent;
|
||||
triggerChangeDocumentTitle(title);
|
||||
} catch (e) {}
|
||||
} catch (e) {
|
||||
//
|
||||
}
|
||||
}, 50),
|
||||
});
|
||||
const [mentionUsersSettingVisible, toggleMentionUsersSettingVisible] = useToggle(false);
|
||||
|
@ -98,7 +99,7 @@ export const Editor: React.FC<IProps> = ({ user: currentUser, documentId, author
|
|||
destoryProvider(provider, 'EDITOR');
|
||||
destoryIndexdbProvider(documentId);
|
||||
};
|
||||
}, []);
|
||||
}, [documentId, provider]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!editor) return;
|
||||
|
@ -157,7 +158,7 @@ export const Editor: React.FC<IProps> = ({ user: currentUser, documentId, author
|
|||
Router.events.off('routeChangeStart', handler);
|
||||
window.removeEventListener('unload', handler);
|
||||
};
|
||||
}, [editor, users, currentUser]);
|
||||
}, [editor, users, currentUser, toggleMentionUsersSettingVisible]);
|
||||
|
||||
useEffect(() => {
|
||||
const listener = (event: KeyboardEvent) => {
|
||||
|
|
|
@ -27,7 +27,6 @@ interface IProps {
|
|||
}
|
||||
|
||||
export const DocumentEditor: React.FC<IProps> = ({ documentId }) => {
|
||||
if (!documentId) return null;
|
||||
const { width: windowWith } = useWindowSize();
|
||||
const { width, fontSize } = useDocumentStyle();
|
||||
const editorWrapClassNames = useMemo(() => {
|
||||
|
@ -42,7 +41,7 @@ export const DocumentEditor: React.FC<IProps> = ({ documentId }) => {
|
|||
Router.push({
|
||||
pathname: `/wiki/${document.wikiId}/document/${documentId}`,
|
||||
});
|
||||
}, [document]);
|
||||
}, [document, documentId]);
|
||||
|
||||
const DocumentTitle = (
|
||||
<>
|
||||
|
|
|
@ -49,7 +49,7 @@ export const Editor: React.FC<IProps> = ({ user, documentId, document, children
|
|||
},
|
||||
},
|
||||
});
|
||||
}, [documentId, user.token]);
|
||||
}, [documentId, user, toggleLoading]);
|
||||
const editor = useEditor({
|
||||
editable: false,
|
||||
extensions: [
|
||||
|
@ -68,7 +68,7 @@ export const Editor: React.FC<IProps> = ({ user, documentId, document, children
|
|||
return () => {
|
||||
destoryProvider(provider, 'READER');
|
||||
};
|
||||
}, []);
|
||||
}, [provider]);
|
||||
|
||||
return (
|
||||
<DataRender
|
||||
|
|
|
@ -64,7 +64,7 @@ export const DocumentPublicReader: React.FC<IProps> = ({ documentId, hideLogo =
|
|||
hasCancel: false,
|
||||
maskClosable: false,
|
||||
onOk() {
|
||||
const $input = document.querySelector('#js-share-document-password') as HTMLInputElement;
|
||||
const $input = document.querySelector('#js-share-document-password');
|
||||
query($input.value);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -12,7 +12,7 @@ export const ImageViewer: React.FC<IProps> = ({ container, containerSelector })
|
|||
if (!el) {
|
||||
return null;
|
||||
}
|
||||
const viewer = new Viewer(el as HTMLElement, { inline: false });
|
||||
const viewer = new Viewer(el, { inline: false });
|
||||
const io = new MutationObserver(() => {
|
||||
viewer.update();
|
||||
});
|
||||
|
|
|
@ -117,25 +117,32 @@ const MessageBox = () => {
|
|||
Notification.info({
|
||||
title: '消息通知',
|
||||
content: (
|
||||
<Link href={msg.url}>
|
||||
<a className={styles.item}>
|
||||
<div className={styles.leftWrap}>
|
||||
<Text
|
||||
ellipsis={{
|
||||
showTooltip: {
|
||||
opts: { content: msg.message },
|
||||
},
|
||||
}}
|
||||
style={{ width: 240 }}
|
||||
>
|
||||
{msg.title}
|
||||
</Text>
|
||||
</div>
|
||||
</a>
|
||||
</Link>
|
||||
<>
|
||||
<div>
|
||||
<Text
|
||||
ellipsis={{
|
||||
showTooltip: {
|
||||
opts: { content: msg.message },
|
||||
},
|
||||
}}
|
||||
style={{ width: 240 }}
|
||||
>
|
||||
{msg.title}
|
||||
</Text>
|
||||
</div>
|
||||
<div>
|
||||
<Text link>
|
||||
<Link href={msg.url}>
|
||||
<a className={styles.item} target="_blank">
|
||||
查看详情
|
||||
</a>
|
||||
</Link>
|
||||
</Text>
|
||||
</div>
|
||||
</>
|
||||
),
|
||||
|
||||
duration: 3,
|
||||
showClose: true,
|
||||
onHookClose() {
|
||||
readMessage(msg.id);
|
||||
},
|
||||
|
|
|
@ -43,11 +43,7 @@ import { Iframe } from './menus/iframe';
|
|||
import { Table } from './menus/table';
|
||||
import { Mind } from './menus/mind';
|
||||
|
||||
export const MenuBar: React.FC<{ editor: any }> = ({ editor }) => {
|
||||
if (!editor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const _MenuBar: React.FC<{ editor: any }> = ({ editor }) => {
|
||||
return (
|
||||
<div>
|
||||
<Space spacing={2}>
|
||||
|
@ -87,8 +83,8 @@ export const MenuBar: React.FC<{ editor: any }> = ({ editor }) => {
|
|||
<Divider />
|
||||
|
||||
<Emoji editor={editor} />
|
||||
<Link editor={editor} />
|
||||
<Blockquote editor={editor} />
|
||||
<Link editor={editor} />
|
||||
<HorizontalRule editor={editor} />
|
||||
<Search editor={editor} />
|
||||
|
||||
|
@ -107,11 +103,11 @@ export const MenuBar: React.FC<{ editor: any }> = ({ editor }) => {
|
|||
);
|
||||
};
|
||||
|
||||
export const CommentMenuBar: React.FC<{ editor: any }> = ({ editor }) => {
|
||||
if (!editor) {
|
||||
return null;
|
||||
}
|
||||
export const MenuBar = React.memo(_MenuBar, (prevProps, nextProps) => {
|
||||
return prevProps.editor === nextProps.editor;
|
||||
});
|
||||
|
||||
const _CommentMenuBar: React.FC<{ editor: any }> = ({ editor }) => {
|
||||
return (
|
||||
<>
|
||||
<Space spacing={2}>
|
||||
|
@ -135,3 +131,7 @@ export const CommentMenuBar: React.FC<{ editor: any }> = ({ editor }) => {
|
|||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const CommentMenuBar = React.memo(_CommentMenuBar, (prevProps, nextProps) => {
|
||||
return prevProps.editor === nextProps.editor;
|
||||
});
|
||||
|
|
|
@ -9,10 +9,6 @@ import { ColorPicker } from '../_components/color-picker';
|
|||
export const BackgroundColor: React.FC<{ editor: Editor }> = ({ editor }) => {
|
||||
const { backgroundColor } = editor.getAttributes('textStyle');
|
||||
|
||||
if (!editor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<ColorPicker
|
||||
onSetColor={(color) => {
|
||||
|
|
|
@ -6,10 +6,6 @@ import { Tooltip } from 'components/tooltip';
|
|||
import { isTitleActive } from 'tiptap/prose-utils';
|
||||
|
||||
export const Bold: React.FC<{ editor: Editor }> = ({ editor }) => {
|
||||
if (!editor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Tooltip content="粗体">
|
||||
<Button
|
||||
|
|
|
@ -5,10 +5,6 @@ import { IconClear } from 'components/icons';
|
|||
import { Tooltip } from 'components/tooltip';
|
||||
|
||||
export const CleadrNodeAndMarks: React.FC<{ editor: Editor }> = ({ editor }) => {
|
||||
if (!editor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Tooltip content="清除格式">
|
||||
<Button
|
||||
|
|
|
@ -6,10 +6,6 @@ import { Tooltip } from 'components/tooltip';
|
|||
import { isTitleActive } from 'tiptap/prose-utils';
|
||||
|
||||
export const Code: React.FC<{ editor: Editor }> = ({ editor }) => {
|
||||
if (!editor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Tooltip content="行内代码">
|
||||
<Button
|
||||
|
|
|
@ -163,10 +163,6 @@ export const Insert: React.FC<{ editor: Editor }> = ({ editor }) => {
|
|||
setRecentUsed(transformToCommands(insertMenuLRUCache.get() as string[]));
|
||||
}, [visible, transformToCommands]);
|
||||
|
||||
if (!editor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
zIndex={10000}
|
||||
|
@ -181,13 +177,13 @@ export const Insert: React.FC<{ editor: Editor }> = ({ editor }) => {
|
|||
}}
|
||||
render={
|
||||
<Dropdown.Menu>
|
||||
{renderedCommands.map((command) => {
|
||||
{renderedCommands.map((command, index) => {
|
||||
return command.title ? (
|
||||
<Dropdown.Title>{command.title}</Dropdown.Title>
|
||||
<Dropdown.Title key={'title' + index}>{command.title}</Dropdown.Title>
|
||||
) : command.custom ? (
|
||||
command.custom(editor, runCommand)
|
||||
) : (
|
||||
<Dropdown.Item onClick={runCommand(command)}>
|
||||
<Dropdown.Item key={command.label} onClick={runCommand(command)}>
|
||||
{command.icon}
|
||||
{command.label}
|
||||
</Dropdown.Item>
|
||||
|
|
|
@ -6,10 +6,6 @@ import { Tooltip } from 'components/tooltip';
|
|||
import { isTitleActive } from 'tiptap/prose-utils';
|
||||
|
||||
export const Italic: React.FC<{ editor: Editor }> = ({ editor }) => {
|
||||
if (!editor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Tooltip content="斜体">
|
||||
<Button
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import { useEffect, useState, useRef, useCallback } from 'react';
|
||||
import { Space, Button } from '@douyinfe/semi-ui';
|
||||
import { IconExternalOpen, IconUnlink, IconEdit } from '@douyinfe/semi-icons';
|
||||
|
@ -12,7 +11,6 @@ import { triggerOpenLinkSettingModal } from '../_event';
|
|||
export const LinkBubbleMenu = ({ editor }) => {
|
||||
const attrs = editor.getAttributes(Link.name);
|
||||
const { href, target } = attrs;
|
||||
const isLinkActive = editor.isActive(Link.name);
|
||||
const [text, setText] = useState();
|
||||
const [from, setFrom] = useState(-1);
|
||||
const [to, setTo] = useState(-1);
|
||||
|
@ -28,30 +26,40 @@ export const LinkBubbleMenu = ({ editor }) => {
|
|||
const unsetLink = useCallback(() => editor.chain().extendMarkRange(Link.name).unsetLink().run(), [editor]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isLinkActive) return;
|
||||
const listener = () => {
|
||||
const isLinkActive = editor.isActive(Link.name);
|
||||
|
||||
const { state } = editor;
|
||||
const isInLink = isMarkActive(state.schema.marks.link)(state);
|
||||
if (!isLinkActive) return;
|
||||
|
||||
if (!isInLink) return;
|
||||
const { state } = editor;
|
||||
const isInLink = isMarkActive(state.schema.marks.link)(state);
|
||||
|
||||
const { $head } = editor.state.selection;
|
||||
const marks = $head.marks();
|
||||
if (!marks.length) return;
|
||||
if (!isInLink) return;
|
||||
|
||||
const mark = marks[0];
|
||||
const node = $head.node($head.depth);
|
||||
const startPosOfThisLine = $head.pos - (($head.nodeBefore && $head.nodeBefore.nodeSize) || 0);
|
||||
const endPosOfThisLine = $head.nodeAfter
|
||||
? startPosOfThisLine + $head.nodeAfter.nodeSize
|
||||
: $head.pos - $head.parentOffset + node.content.size;
|
||||
const { $head } = editor.state.selection;
|
||||
const marks = $head.marks();
|
||||
if (!marks.length) return;
|
||||
|
||||
const { start, end } = findMarkPosition(state, mark, startPosOfThisLine, endPosOfThisLine);
|
||||
const text = state.doc.textBetween(start, end);
|
||||
setText(text);
|
||||
setFrom(start);
|
||||
setTo(end);
|
||||
});
|
||||
const mark = marks[0];
|
||||
const node = $head.node($head.depth);
|
||||
const startPosOfThisLine = $head.pos - (($head.nodeBefore && $head.nodeBefore.nodeSize) || 0);
|
||||
const endPosOfThisLine = $head.nodeAfter
|
||||
? startPosOfThisLine + $head.nodeAfter.nodeSize
|
||||
: $head.pos - $head.parentOffset + node.content.size;
|
||||
|
||||
const { start, end } = findMarkPosition(state, mark, startPosOfThisLine, endPosOfThisLine);
|
||||
const text = state.doc.textBetween(start, end);
|
||||
setText(text);
|
||||
setFrom(start);
|
||||
setTo(end);
|
||||
};
|
||||
|
||||
editor.on('selectionUpdate', listener);
|
||||
|
||||
return () => {
|
||||
editor.off('selectionUpdate', listener);
|
||||
};
|
||||
}, [editor]);
|
||||
|
||||
return (
|
||||
<BubbleMenu
|
||||
|
|
|
@ -9,10 +9,6 @@ import { LinkBubbleMenu } from './bubble';
|
|||
import { LinkSettingModal } from './modal';
|
||||
|
||||
export const Link: React.FC<{ editor: Editor }> = ({ editor }) => {
|
||||
if (!editor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Tooltip content="插入链接">
|
||||
|
|
|
@ -21,6 +21,9 @@ export const LinkSettingModal: React.FC<IProps> = ({ editor }) => {
|
|||
|
||||
const { from, to } = initialState;
|
||||
const { view } = editor;
|
||||
|
||||
console.log(from, to);
|
||||
|
||||
const schema = view.state.schema;
|
||||
const node = schema.text(values.text, [schema.marks.link.create({ href: values.href })]);
|
||||
view.dispatch(view.state.tr.replaceRangeWith(from, to, node));
|
||||
|
|
|
@ -5,10 +5,6 @@ import { IconRedo } from '@douyinfe/semi-icons';
|
|||
import { Tooltip } from 'components/tooltip';
|
||||
|
||||
export const Redo: React.FC<{ editor: Editor }> = ({ editor }) => {
|
||||
if (!editor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Tooltip content="撤销">
|
||||
<Button
|
||||
|
|
|
@ -6,10 +6,6 @@ import { Tooltip } from 'components/tooltip';
|
|||
import { isTitleActive } from 'tiptap/prose-utils';
|
||||
|
||||
export const Strike: React.FC<{ editor: Editor }> = ({ editor }) => {
|
||||
if (!editor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Tooltip content="删除线">
|
||||
<Button
|
||||
|
|
|
@ -5,10 +5,6 @@ import { Tooltip } from 'components/tooltip';
|
|||
import { isTitleActive } from 'tiptap/prose-utils';
|
||||
|
||||
export const Subscript: React.FC<{ editor: any }> = ({ editor }) => {
|
||||
if (!editor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Tooltip content="下标">
|
||||
<Button
|
||||
|
|
|
@ -5,10 +5,6 @@ import { Tooltip } from 'components/tooltip';
|
|||
import { isTitleActive } from 'tiptap/prose-utils';
|
||||
|
||||
export const Superscript: React.FC<{ editor: any }> = ({ editor }) => {
|
||||
if (!editor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Tooltip content="上标">
|
||||
<Button
|
||||
|
|
|
@ -9,10 +9,6 @@ import { ColorPicker } from '../_components/color-picker';
|
|||
export const TextColor: React.FC<{ editor: Editor }> = ({ editor }) => {
|
||||
const { color } = editor.getAttributes('textStyle');
|
||||
|
||||
if (!editor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<ColorPicker
|
||||
onSetColor={(color) => {
|
||||
|
|
|
@ -6,10 +6,6 @@ import { Tooltip } from 'components/tooltip';
|
|||
import { isTitleActive } from 'tiptap/prose-utils';
|
||||
|
||||
export const Underline: React.FC<{ editor: Editor }> = ({ editor }) => {
|
||||
if (!editor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Tooltip content="下划线">
|
||||
<Button
|
||||
|
|
|
@ -5,10 +5,6 @@ import { IconUndo } from '@douyinfe/semi-icons';
|
|||
import { Tooltip } from 'components/tooltip';
|
||||
|
||||
export const Undo: React.FC<{ editor: Editor }> = ({ editor }) => {
|
||||
if (!editor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Tooltip content="撤销">
|
||||
<Button
|
||||
|
|
|
@ -19,6 +19,7 @@ export const extractMarkAttributesFromMatch = ([, , , attrsString]) => {
|
|||
|
||||
export function findMarkPosition(state: EditorState, mark, from, to) {
|
||||
let markPos = { start: -1, end: -1 };
|
||||
|
||||
state.doc.nodesBetween(from, to, (node, pos) => {
|
||||
if (markPos.start > -1) {
|
||||
return false;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
table {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
margin: 0.75em 0 0;
|
||||
overflow: hidden;
|
||||
border-collapse: collapse;
|
||||
|
|
|
@ -40,8 +40,8 @@ export const BubbleMenu: React.FC<BubbleMenuProps> = (props) => {
|
|||
|
||||
editor.registerPlugin(plugin);
|
||||
return () => editor.unregisterPlugin(pluginKey);
|
||||
// TODO: 检验是否应该是 props.editor
|
||||
}, [props, element]);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [props.editor, element]);
|
||||
|
||||
return (
|
||||
<div ref={setElement} className={props.className} style={{ visibility: 'hidden' }}>
|
||||
|
|
|
@ -196,7 +196,7 @@ function MindElixir(
|
|||
mobileMenu,
|
||||
}: Options
|
||||
) {
|
||||
const box = document.querySelector(el) as HTMLElement;
|
||||
const box = document.querySelector(el);
|
||||
if (!box) return;
|
||||
this.mindElixirBox = box;
|
||||
this.before = before || {};
|
||||
|
|
|
@ -103,7 +103,7 @@ export function createInputDiv(tpc: Topic) {
|
|||
console.time('createInputDiv');
|
||||
if (!tpc) return;
|
||||
let div = $d.createElement('div');
|
||||
const origin = tpc.childNodes[0].textContent as string;
|
||||
const origin = tpc.childNodes[0].textContent;
|
||||
tpc.appendChild(div);
|
||||
div.id = 'input-box';
|
||||
div.innerText = origin;
|
||||
|
@ -138,7 +138,7 @@ export function createInputDiv(tpc: Topic) {
|
|||
if (!div) return; // 防止重复blur
|
||||
const node = tpc.nodeObj;
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const topic = div.textContent!.trim();
|
||||
const topic = div.textContent.trim();
|
||||
if (topic === '') node.topic = origin;
|
||||
else node.topic = topic;
|
||||
div.remove();
|
||||
|
|
|
@ -31,5 +31,5 @@
|
|||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "global.d.ts", "**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["node_modules"]
|
||||
"exclude": ["node_modules", "next.config.js"]
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue