mirror of https://github.com/fantasticit/think.git
refactor: lint code
parent
76cdcff589
commit
d0165bad1e
|
@ -20,6 +20,7 @@ export const Blockquote = BuiltInBlockquote.extend({
|
|||
addInputRules() {
|
||||
const multilineInputRegex = /^\s*>>>\s$/gm;
|
||||
return [
|
||||
// eslint-disable-next-line no-unsafe-optional-chaining
|
||||
...this.parent?.(),
|
||||
wrappingInputRule({
|
||||
find: multilineInputRegex,
|
||||
|
|
|
@ -101,7 +101,9 @@ const lockCollaborationUserEditingNodes = (extensionThis, users) => {
|
|||
options.collaborationUserCursorCache.set(user.clientId, { user, cursor });
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
} catch (e) {
|
||||
//
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -61,7 +61,7 @@ export const Paste = Extension.create({
|
|||
|
||||
if (node) {
|
||||
const doc = safeJSONParse(node);
|
||||
let tr = view.state.tr;
|
||||
const tr = view.state.tr;
|
||||
const selection = tr.selection;
|
||||
view.dispatch(tr.insert(selection.from - 1, view.state.schema.nodeFromJSON(doc)).scrollIntoView());
|
||||
return true;
|
||||
|
|
|
@ -182,7 +182,7 @@ const gotoSearchResult = ({ view, tr, searchResults, searchResultCurrentClass, g
|
|||
const result = searchResults[gotoIndex];
|
||||
|
||||
if (result) {
|
||||
let transaction = tr.setMeta('directDecoration', {
|
||||
const transaction = tr.setMeta('directDecoration', {
|
||||
fromPos: result.from,
|
||||
toPos: result.to,
|
||||
attrs: { class: searchResultCurrentClass },
|
||||
|
|
|
@ -116,7 +116,7 @@ export class Renderer {
|
|||
}
|
||||
|
||||
renderChildren(node) {
|
||||
let nodes = [];
|
||||
const nodes = [];
|
||||
|
||||
node.childNodes.forEach((child) => {
|
||||
const NodeClass = this.getMatchingNode(child);
|
||||
|
@ -180,7 +180,7 @@ export class Renderer {
|
|||
}
|
||||
|
||||
getMatchingClass(node, classes) {
|
||||
for (let i in classes) {
|
||||
for (const i in classes) {
|
||||
const Class = classes[i];
|
||||
const instance = new Class(node);
|
||||
if (instance.matching()) {
|
||||
|
|
|
@ -18,6 +18,7 @@ export * from './markdown-source-map';
|
|||
const extractImage = (html) => {
|
||||
let matches = [];
|
||||
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
while ((matches = html.match(/\<p.*?\>\<img(.|\s)*?\>\<\/p\>/g))) {
|
||||
const target = matches[0].match(/<img.*?>/)[0];
|
||||
html = html.replace(matches[0], target);
|
||||
|
|
|
@ -20,7 +20,7 @@ export const createMarkdownContainer = (types: string | Array<string>) => (md) =
|
|||
if (tag.nesting === 1) {
|
||||
tag.attrSet('class', type);
|
||||
|
||||
var m = tag.info.trim().match(regexp);
|
||||
const m = tag.info.trim().match(regexp);
|
||||
if (m[1]) {
|
||||
const data = strToJSON(m[1]);
|
||||
jsonToDOMDataset(data).forEach(({ key, value }) => {
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
/* eslint-disable */
|
||||
// var katex = require('katex');
|
||||
|
||||
// Test if potential opening or closing delimieter
|
||||
// Assumes that there is a "$" at state.src[pos]
|
||||
function isValidDelim(state, pos) {
|
||||
var prevChar,
|
||||
let prevChar,
|
||||
nextChar,
|
||||
max = state.posMax,
|
||||
can_open = true,
|
||||
|
@ -32,7 +33,7 @@ function isValidDelim(state, pos) {
|
|||
}
|
||||
|
||||
function math_inline(state, silent) {
|
||||
var start, match, token, res, pos, esc_count;
|
||||
let start, match, token, res, pos, esc_count;
|
||||
|
||||
if (state.src[state.pos] !== '$') {
|
||||
return false;
|
||||
|
@ -107,7 +108,7 @@ function math_inline(state, silent) {
|
|||
}
|
||||
|
||||
function math_block(state, start, end, silent) {
|
||||
var firstLine,
|
||||
let firstLine,
|
||||
lastLine,
|
||||
next,
|
||||
lastPos,
|
||||
|
@ -179,7 +180,7 @@ function escapeHtml(unsafe) {
|
|||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
var katex = {
|
||||
let katex = {
|
||||
renderToString: (s, opts) => s,
|
||||
};
|
||||
|
||||
|
@ -194,7 +195,7 @@ export default function math_plugin(md, options) {
|
|||
options.blockClass = '';
|
||||
}
|
||||
|
||||
var inlineRenderer = function (tokens, idx) {
|
||||
const inlineRenderer = function (tokens, idx) {
|
||||
return katexBlock(tokens[idx].content);
|
||||
};
|
||||
|
||||
|
@ -212,7 +213,7 @@ export default function math_plugin(md, options) {
|
|||
}
|
||||
};
|
||||
|
||||
var blockRenderer = function (tokens, idx) {
|
||||
const blockRenderer = function (tokens, idx) {
|
||||
return katexBlock(tokens[idx].content) + '\n';
|
||||
};
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
/* eslint-disable */
|
||||
|
||||
// Copied from https://github.com/markdown-it/markdown-it/blob/master/lib/rules_block/table.js
|
||||
|
||||
function isSpace(code) {
|
||||
|
@ -10,14 +12,14 @@ function isSpace(code) {
|
|||
}
|
||||
|
||||
function getLine(state, line) {
|
||||
var pos = state.bMarks[line] + state.tShift[line],
|
||||
const pos = state.bMarks[line] + state.tShift[line],
|
||||
max = state.eMarks[line];
|
||||
|
||||
return state.src.substr(pos, max - pos);
|
||||
}
|
||||
|
||||
function escapedSplit(str) {
|
||||
var result = [],
|
||||
let result = [],
|
||||
pos = 0,
|
||||
max = str.length,
|
||||
ch,
|
||||
|
@ -53,7 +55,7 @@ function escapedSplit(str) {
|
|||
}
|
||||
|
||||
function table(state, startLine, endLine, silent) {
|
||||
var ch,
|
||||
let ch,
|
||||
lineText,
|
||||
pos,
|
||||
i,
|
||||
|
|
|
@ -50,8 +50,8 @@ export default function markdownItTaskLists(
|
|||
}
|
||||
|
||||
function attrSet(token, name, value) {
|
||||
var index = token.attrIndex(name);
|
||||
var attr = [name, value];
|
||||
const index = token.attrIndex(name);
|
||||
const attr = [name, value];
|
||||
|
||||
if (index < 0) {
|
||||
token.attrPush(attr);
|
||||
|
|
|
@ -130,6 +130,7 @@ const SerializerConfig = {
|
|||
[Image.name]: renderImage,
|
||||
[Katex.name]: (state, node) => {
|
||||
state.ensureNewLine();
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
state.write(`\$\$${node.attrs.text || ''}\$\$`);
|
||||
state.closeBlock(node);
|
||||
},
|
||||
|
|
|
@ -17,7 +17,7 @@ export const Size: React.FC<{ width: number; maxWidth?: number; height: number;
|
|||
$form.current.validate().then((values) => {
|
||||
onOk(values as ISize);
|
||||
});
|
||||
}, []);
|
||||
}, [onOk]);
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
|
|
|
@ -17,7 +17,7 @@ export const CountdownSettingModal: React.FC<IProps> = ({ editor }) => {
|
|||
editor.chain().focus().setCountdown({ title: values.title, date: values.date.valueOf() }).run();
|
||||
toggleVisible(false);
|
||||
});
|
||||
}, []);
|
||||
}, [editor, toggleVisible]);
|
||||
|
||||
useEffect(() => {
|
||||
const handler = (data) => {
|
||||
|
@ -30,7 +30,7 @@ export const CountdownSettingModal: React.FC<IProps> = ({ editor }) => {
|
|||
return () => {
|
||||
event.off(OPEN_COUNT_SETTING_MODAL, handler);
|
||||
};
|
||||
}, []);
|
||||
}, [toggleVisible]);
|
||||
|
||||
return (
|
||||
<Modal centered title="倒计时" visible={visible} onOk={handleOk} onCancel={() => toggleVisible(false)}>
|
||||
|
|
|
@ -6,11 +6,14 @@ import { IconEmoji } from 'components/icons';
|
|||
import { EmojiPicker } from 'components/emoji-picker';
|
||||
|
||||
export const Emoji: React.FC<{ editor: Editor }> = ({ editor }) => {
|
||||
const setEmoji = useCallback((emoji) => {
|
||||
const { selection } = editor.state;
|
||||
const { $anchor } = selection;
|
||||
return editor.chain().insertContentAt($anchor.pos, emoji).run();
|
||||
}, []);
|
||||
const setEmoji = useCallback(
|
||||
(emoji) => {
|
||||
const { selection } = editor.state;
|
||||
const { $anchor } = selection;
|
||||
return editor.chain().insertContentAt($anchor.pos, emoji).run();
|
||||
},
|
||||
[editor]
|
||||
);
|
||||
|
||||
return (
|
||||
<EmojiPicker onSelectEmoji={setEmoji}>
|
||||
|
|
|
@ -9,13 +9,16 @@ export const FontSize: React.FC<{ editor: Editor }> = ({ editor }) => {
|
|||
const currentFontSizePx = editor.getAttributes('textStyle').fontSize || '16px';
|
||||
const currentFontSize = +currentFontSizePx.replace('px', '');
|
||||
|
||||
const toggle = useCallback((val) => {
|
||||
editor
|
||||
.chain()
|
||||
.focus()
|
||||
.setFontSize(val + 'px')
|
||||
.run();
|
||||
}, []);
|
||||
const toggle = useCallback(
|
||||
(val) => {
|
||||
editor
|
||||
.chain()
|
||||
.focus()
|
||||
.setFontSize(val + 'px')
|
||||
.run();
|
||||
},
|
||||
[editor]
|
||||
);
|
||||
|
||||
return (
|
||||
<Select
|
||||
|
|
|
@ -14,13 +14,16 @@ const getCurrentCaretTitle = (editor) => {
|
|||
};
|
||||
|
||||
export const Heading: React.FC<{ editor: Editor }> = ({ editor }) => {
|
||||
const toggle = useCallback((level) => {
|
||||
if (level === 'paragraph') {
|
||||
editor.chain().focus().setParagraph().run();
|
||||
} else {
|
||||
editor.chain().focus().toggleHeading({ level }).run();
|
||||
}
|
||||
}, []);
|
||||
const toggle = useCallback(
|
||||
(level) => {
|
||||
if (level === 'paragraph') {
|
||||
editor.chain().focus().setParagraph().run();
|
||||
} else {
|
||||
editor.chain().focus().toggleHeading({ level }).run();
|
||||
}
|
||||
},
|
||||
[editor]
|
||||
);
|
||||
|
||||
return (
|
||||
<Select
|
||||
|
|
|
@ -27,7 +27,7 @@ export const IframeBubbleMenu = ({ editor }) => {
|
|||
|
||||
const handleCancel = useCallback(() => {
|
||||
toggleVisible(false);
|
||||
}, []);
|
||||
}, [toggleVisible]);
|
||||
|
||||
const handleOk = useCallback(() => {
|
||||
$form.current.validate().then((values) => {
|
||||
|
@ -41,7 +41,7 @@ export const IframeBubbleMenu = ({ editor }) => {
|
|||
.run();
|
||||
toggleVisible(false);
|
||||
});
|
||||
}, []);
|
||||
}, [editor, toggleVisible]);
|
||||
|
||||
const visitLink = useCallback(() => {
|
||||
window.open(url, '_blank');
|
||||
|
@ -49,7 +49,7 @@ export const IframeBubbleMenu = ({ editor }) => {
|
|||
|
||||
const openEditLinkModal = useCallback(() => {
|
||||
toggleVisible(true);
|
||||
}, []);
|
||||
}, [toggleVisible]);
|
||||
|
||||
const setSize = useCallback(
|
||||
(size) => {
|
||||
|
|
|
@ -154,14 +154,14 @@ export const Insert: React.FC<{ editor: Editor }> = ({ editor }) => {
|
|||
toggleVisible(false);
|
||||
};
|
||||
},
|
||||
[editor, toggleVisible]
|
||||
[editor, toggleVisible, transformToCommands, user]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!visible) return;
|
||||
insertMenuLRUCache.syncFromStorage();
|
||||
setRecentUsed(transformToCommands(insertMenuLRUCache.get() as string[]));
|
||||
}, [visible]);
|
||||
}, [visible, transformToCommands]);
|
||||
|
||||
if (!editor) {
|
||||
return null;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
/* 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';
|
||||
|
|
|
@ -27,7 +27,7 @@ export const LinkSettingModal: React.FC<IProps> = ({ editor }) => {
|
|||
view.dispatch(view.state.tr.scrollIntoView());
|
||||
toggleVisible(false);
|
||||
});
|
||||
}, [initialState]);
|
||||
}, [initialState, editor, toggleVisible]);
|
||||
|
||||
useEffect(() => {
|
||||
const handler = (data) => {
|
||||
|
@ -40,7 +40,7 @@ export const LinkSettingModal: React.FC<IProps> = ({ editor }) => {
|
|||
return () => {
|
||||
event.off(OPEN_LINK_SETTING_MODAL, handler);
|
||||
};
|
||||
}, [editor]);
|
||||
}, [editor, toggleVisible]);
|
||||
|
||||
return (
|
||||
<Modal title="编辑链接" visible={visible} onOk={handleOk} onCancel={() => toggleVisible(false)} centered>
|
||||
|
|
|
@ -15,12 +15,16 @@ export const Search: React.FC<{ editor: Editor }> = ({ editor }) => {
|
|||
const [replaceValue, setReplaceValue] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
editor?.commands?.setSearchTerm(searchValue);
|
||||
}, [searchValue]);
|
||||
if (editor && editor.commands && editor.commands.setSearchTerm) {
|
||||
editor.commands.setSearchTerm(searchValue);
|
||||
}
|
||||
}, [searchValue, editor]);
|
||||
|
||||
useEffect(() => {
|
||||
editor?.commands?.setReplaceTerm(replaceValue);
|
||||
}, [replaceValue]);
|
||||
if (editor && editor.commands && editor.commands.setReplaceTerm) {
|
||||
editor.commands.setReplaceTerm(replaceValue);
|
||||
}
|
||||
}, [replaceValue, editor]);
|
||||
|
||||
return (
|
||||
<Popover
|
||||
|
|
|
@ -44,7 +44,9 @@ export function copyNode(nodeOrNodeName: string | Node, editor?: Editor) {
|
|||
toCopy.push({ text: markdown, format: 'text/markdown' });
|
||||
const html = markdownToHTML(markdown);
|
||||
toCopy.push({ text: html, format: 'text/html' });
|
||||
} catch (e) {}
|
||||
} catch (e) {
|
||||
//
|
||||
}
|
||||
|
||||
copy(toCopy);
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ export const jsonToDOMDataset = (json: Record<string, unknown>) => {
|
|||
* @param transformToJSON 是否要转为 JSON
|
||||
*/
|
||||
export const getDatasetAttribute =
|
||||
(attribute: string, transformToJSON: boolean = false) =>
|
||||
(attribute: string, transformToJSON = false) =>
|
||||
(element: HTMLElement) => {
|
||||
const dataKey = attribute.startsWith('data-') ? attribute : `data-${attribute}`;
|
||||
const value = decodeURIComponent(element.getAttribute(dataKey));
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
export function isValidURL(str) {
|
||||
var pattern = new RegExp(
|
||||
const pattern = new RegExp(
|
||||
'^(https?:\\/\\/)?' + // protocol
|
||||
'((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // domain name
|
||||
'((\\d{1,3}\\.){3}\\d{1,3}))' + // OR ip (v4) address
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
export function uuid() {
|
||||
var s = [];
|
||||
const s = [];
|
||||
|
||||
var hexDigits = '0123456789abcdef';
|
||||
const hexDigits = '0123456789abcdef';
|
||||
|
||||
for (var i = 0; i < 36; i++) {
|
||||
for (let i = 0; i < 36; i++) {
|
||||
s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
|
||||
}
|
||||
|
||||
s[14] = '4'; // bits 12-15 of the time_hi_and_version field to 0010
|
||||
s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
|
||||
s[8] = s[13] = s[18] = s[23] = '-';
|
||||
var uuid = s.join('');
|
||||
const uuid = s.join('');
|
||||
return uuid;
|
||||
}
|
||||
|
|
|
@ -48,7 +48,6 @@ export const getProvider = ({
|
|||
docType,
|
||||
},
|
||||
maxAttempts: 5,
|
||||
forceSyncInterval: 100,
|
||||
...events,
|
||||
} as any);
|
||||
pool.set(targetId, provider);
|
||||
|
|
|
@ -40,7 +40,8 @@ export const BubbleMenu: React.FC<BubbleMenuProps> = (props) => {
|
|||
|
||||
editor.registerPlugin(plugin);
|
||||
return () => editor.unregisterPlugin(pluginKey);
|
||||
}, [props.editor, element]);
|
||||
// TODO: 检验是否应该是 props.editor
|
||||
}, [props, element]);
|
||||
|
||||
return (
|
||||
<div ref={setElement} className={props.className} style={{ visibility: 'hidden' }}>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { useEffect, useRef } from 'react';
|
||||
import { useCallback, useEffect, useRef } from 'react';
|
||||
import cls from 'classnames';
|
||||
import { NodeViewWrapper } from '@tiptap/react';
|
||||
import { Button, Typography, Spin, Collapsible, Space } from '@douyinfe/semi-ui';
|
||||
|
@ -20,36 +20,39 @@ export const AttachmentWrapper = ({ editor, node, updateAttributes }) => {
|
|||
const [loading, toggleLoading] = useToggle(false);
|
||||
const [visible, toggleVisible] = useToggle(false);
|
||||
|
||||
const selectFile = () => {
|
||||
const selectFile = useCallback(() => {
|
||||
if (!isEditable || url) return;
|
||||
isEditable && $upload.current.click();
|
||||
};
|
||||
}, [isEditable, url]);
|
||||
|
||||
const handleFile = async (e) => {
|
||||
const file = e.target.files && e.target.files[0];
|
||||
const fileInfo = {
|
||||
fileName: extractFilename(file.name),
|
||||
fileSize: file.size,
|
||||
fileType: file.type,
|
||||
fileExt: extractFileExtension(file.name),
|
||||
};
|
||||
toggleLoading(true);
|
||||
try {
|
||||
const url = await uploadFile(file);
|
||||
updateAttributes({ ...fileInfo, url });
|
||||
toggleLoading(false);
|
||||
} catch (error) {
|
||||
updateAttributes({ error: '文件上传失败:' + (error && error.message) || '未知错误' });
|
||||
toggleLoading(false);
|
||||
}
|
||||
};
|
||||
const handleFile = useCallback(
|
||||
async (e) => {
|
||||
const file = e.target.files && e.target.files[0];
|
||||
const fileInfo = {
|
||||
fileName: extractFilename(file.name),
|
||||
fileSize: file.size,
|
||||
fileType: file.type,
|
||||
fileExt: extractFileExtension(file.name),
|
||||
};
|
||||
toggleLoading(true);
|
||||
try {
|
||||
const url = await uploadFile(file);
|
||||
updateAttributes({ ...fileInfo, url });
|
||||
toggleLoading(false);
|
||||
} catch (error) {
|
||||
updateAttributes({ error: '文件上传失败:' + (error && error.message) || '未知错误' });
|
||||
toggleLoading(false);
|
||||
}
|
||||
},
|
||||
[toggleLoading, updateAttributes]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!url && !hasTrigger) {
|
||||
selectFile();
|
||||
updateAttributes({ hasTrigger: true });
|
||||
}
|
||||
}, [url, hasTrigger]);
|
||||
}, [url, hasTrigger, selectFile, updateAttributes]);
|
||||
|
||||
const content = (() => {
|
||||
if (isEditable && !url) {
|
||||
|
|
|
@ -8,9 +8,12 @@ export const CalloutWrapper = ({ editor, node, updateAttributes }) => {
|
|||
const { isEditable } = editor;
|
||||
const { emoji, textColor, borderColor, backgroundColor } = node.attrs;
|
||||
|
||||
const onSelectEmoji = useCallback((emoji) => {
|
||||
updateAttributes({ emoji });
|
||||
}, []);
|
||||
const onSelectEmoji = useCallback(
|
||||
(emoji) => {
|
||||
updateAttributes({ emoji });
|
||||
},
|
||||
[updateAttributes]
|
||||
);
|
||||
|
||||
return (
|
||||
<NodeViewWrapper id="js-bannber-container" className={cls(styles.wrap)}>
|
||||
|
|
|
@ -17,7 +17,7 @@ export const DocumentChildrenWrapper = ({ editor, node, updateAttributes }) => {
|
|||
const { pathname, query } = useRouter();
|
||||
let { wikiId, documentId } = node.attrs;
|
||||
if (!wikiId) {
|
||||
query?.wikiId;
|
||||
wikiId = query?.wikiId;
|
||||
}
|
||||
if (!documentId) {
|
||||
documentId = query?.documentId;
|
||||
|
@ -31,7 +31,7 @@ export const DocumentChildrenWrapper = ({ editor, node, updateAttributes }) => {
|
|||
if (attrs.wikiId !== wikiId || attrs.documentId !== documentId) {
|
||||
updateAttributes({ wikiId, documentId });
|
||||
}
|
||||
}, [node.attrs, wikiId, documentId]);
|
||||
}, [node.attrs, wikiId, documentId, updateAttributes]);
|
||||
|
||||
return (
|
||||
<NodeViewWrapper
|
||||
|
|
|
@ -44,7 +44,7 @@ export const DocumentReferenceWrapper = ({ editor, node, updateAttributes }) =>
|
|||
</a>
|
||||
</Link>
|
||||
);
|
||||
}, [wikiId, documentId]);
|
||||
}, [wikiId, documentId, isEditable, isShare, title]);
|
||||
|
||||
return (
|
||||
<NodeViewWrapper as="div" className={cls(styles.wrap, isEditable && 'render-wrapper')}>
|
||||
|
|
|
@ -81,3 +81,5 @@ export const EmojiList: React.FC<IProps> = forwardRef((props, ref) => {
|
|||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
EmojiList.displayName = 'EmojiList';
|
||||
|
|
|
@ -13,9 +13,12 @@ export const IframeWrapper = ({ editor, node, updateAttributes }) => {
|
|||
const { url, width, height } = node.attrs;
|
||||
const { width: maxWidth } = getEditorContainerDOMSize(editor);
|
||||
|
||||
const onResize = useCallback((size) => {
|
||||
updateAttributes({ width: size.width, height: size.height });
|
||||
}, []);
|
||||
const onResize = useCallback(
|
||||
(size) => {
|
||||
updateAttributes({ width: size.width, height: size.height });
|
||||
},
|
||||
[updateAttributes]
|
||||
);
|
||||
|
||||
return (
|
||||
<NodeViewWrapper>
|
||||
|
|
|
@ -22,44 +22,50 @@ export const ImageWrapper = ({ editor, node, updateAttributes }) => {
|
|||
const $upload = useRef<HTMLInputElement>();
|
||||
const [loading, toggleLoading] = useToggle(false);
|
||||
|
||||
const onResize = useCallback((size) => {
|
||||
updateAttributes({ height: size.height, width: size.width });
|
||||
}, []);
|
||||
const onResize = useCallback(
|
||||
(size) => {
|
||||
updateAttributes({ height: size.height, width: size.width });
|
||||
},
|
||||
[updateAttributes]
|
||||
);
|
||||
|
||||
const selectFile = useCallback(() => {
|
||||
if (!isEditable || error || src) return;
|
||||
isEditable && $upload.current.click();
|
||||
}, [isEditable, error, src]);
|
||||
|
||||
const handleFile = useCallback(async (e) => {
|
||||
const file = e.target.files && e.target.files[0];
|
||||
const handleFile = useCallback(
|
||||
async (e) => {
|
||||
const file = e.target.files && e.target.files[0];
|
||||
|
||||
const fileInfo = {
|
||||
fileName: extractFilename(file.name),
|
||||
fileSize: file.size,
|
||||
fileType: file.type,
|
||||
fileExt: extractFileExtension(file.name),
|
||||
};
|
||||
const fileInfo = {
|
||||
fileName: extractFilename(file.name),
|
||||
fileSize: file.size,
|
||||
fileType: file.type,
|
||||
fileExt: extractFileExtension(file.name),
|
||||
};
|
||||
|
||||
toggleLoading(true);
|
||||
toggleLoading(true);
|
||||
|
||||
try {
|
||||
const src = await uploadFile(file);
|
||||
const size = await getImageWidthHeight(file);
|
||||
updateAttributes({ ...fileInfo, ...size, src });
|
||||
toggleLoading(false);
|
||||
} catch (error) {
|
||||
updateAttributes({ error: '图片上传失败:' + (error && error.message) || '未知错误' });
|
||||
toggleLoading(false);
|
||||
}
|
||||
}, []);
|
||||
try {
|
||||
const src = await uploadFile(file);
|
||||
const size = await getImageWidthHeight(file);
|
||||
updateAttributes({ ...fileInfo, ...size, src });
|
||||
toggleLoading(false);
|
||||
} catch (error) {
|
||||
updateAttributes({ error: '图片上传失败:' + (error && error.message) || '未知错误' });
|
||||
toggleLoading(false);
|
||||
}
|
||||
},
|
||||
[updateAttributes, toggleLoading]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!src && !hasTrigger) {
|
||||
selectFile();
|
||||
updateAttributes({ hasTrigger: true });
|
||||
}
|
||||
}, [src, hasTrigger]);
|
||||
}, [src, hasTrigger, selectFile, updateAttributes]);
|
||||
|
||||
return (
|
||||
<NodeViewWrapper as="div" style={{ textAlign, fontSize: 0, maxWidth: '100%' }}>
|
||||
|
|
|
@ -32,7 +32,7 @@ export const KatexWrapper = ({ editor, node, updateAttributes }) => {
|
|||
) : (
|
||||
<span contentEditable={false}>点击输入公式</span>
|
||||
),
|
||||
[text]
|
||||
[text, formatText]
|
||||
);
|
||||
|
||||
const onVisibleChange = useCallback(
|
||||
|
@ -42,7 +42,7 @@ export const KatexWrapper = ({ editor, node, updateAttributes }) => {
|
|||
updateAttributes({ defaultShowPicker: false });
|
||||
}
|
||||
},
|
||||
[defaultShowPicker, updateAttributes, createUser, user]
|
||||
[defaultShowPicker, toggleVisible, updateAttributes, createUser, user]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -50,7 +50,7 @@ export const KatexWrapper = ({ editor, node, updateAttributes }) => {
|
|||
toggleVisible(true);
|
||||
setTimeout(() => ref.current?.focus(), 100);
|
||||
}
|
||||
}, [defaultShowPicker, createUser, user]);
|
||||
}, [defaultShowPicker, toggleVisible, createUser, user]);
|
||||
|
||||
return (
|
||||
<NodeViewWrapper as="span" className={cls(styles.wrap, 'render-wrapper')} contentEditable={false}>
|
||||
|
|
|
@ -81,3 +81,5 @@ export const MentionList: React.FC<IProps> = forwardRef((props, ref) => {
|
|||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
MentionList.displayName = 'MentionList';
|
||||
|
|
|
@ -87,3 +87,5 @@ export const MenuList: React.FC<IProps> = forwardRef((props, ref) => {
|
|||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
MenuList.displayName = 'MenuList';
|
||||
|
|
|
@ -47,7 +47,7 @@ export const MindWrapper = ({ editor, node, updateAttributes }) => {
|
|||
style={{ ...INHERIT_SIZE_STYLE, overflow: 'hidden' }}
|
||||
></div>
|
||||
);
|
||||
}, [loading, error, width, height]);
|
||||
}, [loading, error]);
|
||||
|
||||
const onResize = useCallback(
|
||||
(size) => {
|
||||
|
@ -69,11 +69,12 @@ export const MindWrapper = ({ editor, node, updateAttributes }) => {
|
|||
.catch((e) => {
|
||||
setError(e);
|
||||
});
|
||||
}, []);
|
||||
}, [toggleLoading]);
|
||||
|
||||
// 初始化渲染
|
||||
useEffect(() => {
|
||||
if (loading || !$container.current) return;
|
||||
const container = $container.current;
|
||||
if (loading || !container) return;
|
||||
|
||||
const onChange = () => {
|
||||
updateAttributes({ data: mind.getAllData() });
|
||||
|
@ -89,8 +90,8 @@ export const MindWrapper = ({ editor, node, updateAttributes }) => {
|
|||
isEnter = false;
|
||||
};
|
||||
|
||||
$container.current.addEventListener('onmouseenter', onMouseEnter);
|
||||
$container.current.addEventListener('onMouseLeave', onMouseLeave);
|
||||
container.addEventListener('onmouseenter', onMouseEnter);
|
||||
container.addEventListener('onMouseLeave', onMouseLeave);
|
||||
|
||||
try {
|
||||
mind = new window.MindElixir({
|
||||
|
@ -115,16 +116,18 @@ export const MindWrapper = ({ editor, node, updateAttributes }) => {
|
|||
}
|
||||
|
||||
return () => {
|
||||
if ($container.current) {
|
||||
$container.current.removeEventListener('onmouseenter', onMouseEnter);
|
||||
$container.current.removeEventListener('onMouseLeave', onMouseLeave);
|
||||
if (container) {
|
||||
container.removeEventListener('onmouseenter', onMouseEnter);
|
||||
container.removeEventListener('onMouseLeave', onMouseLeave);
|
||||
}
|
||||
|
||||
if (mind) {
|
||||
mind.destroy();
|
||||
}
|
||||
};
|
||||
}, [loading, editor, updateAttributes]);
|
||||
// data 的更新交给下方 effect
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [loading, editor, updateAttributes, toggleLoading]);
|
||||
|
||||
useEffect(() => {
|
||||
const mind = $mind.current;
|
||||
|
|
|
@ -14,8 +14,8 @@ import { SIDE, GAP, TURNPOINT_R, PRIMARY_NODE_HORIZONTAL_GAP, PRIMARY_NODE_VERTI
|
|||
* @param {object} primaryNode process the specific primary node only
|
||||
*/
|
||||
export default function linkDiv(primaryNode) {
|
||||
var primaryNodeHorizontalGap = this.primaryNodeHorizontalGap || PRIMARY_NODE_HORIZONTAL_GAP;
|
||||
var primaryNodeVerticalGap = this.primaryNodeVerticalGap || PRIMARY_NODE_VERTICAL_GAP;
|
||||
const primaryNodeHorizontalGap = this.primaryNodeHorizontalGap || PRIMARY_NODE_HORIZONTAL_GAP;
|
||||
const primaryNodeVerticalGap = this.primaryNodeVerticalGap || PRIMARY_NODE_VERTICAL_GAP;
|
||||
console.time('linkDiv');
|
||||
const root = this.root;
|
||||
root.style.cssText = `top:${10000 - root.offsetHeight / 2}px;left:${10000 - root.offsetWidth / 2}px;`;
|
||||
|
|
|
@ -86,6 +86,7 @@ const Toolbar = ({ mind, toggleBold, setFontColor, setBackgroundColor, setLink }
|
|||
return () => {
|
||||
mind.bus.removeListener('selectNode', listener);
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return (
|
||||
|
|
|
@ -137,6 +137,7 @@ export function createInputDiv(tpc: Topic) {
|
|||
div.addEventListener('blur', () => {
|
||||
if (!div) return; // 防止重复blur
|
||||
const node = tpc.nodeObj;
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const topic = div.textContent!.trim();
|
||||
if (topic === '') node.topic = origin;
|
||||
else node.topic = topic;
|
||||
|
|
|
@ -51,11 +51,12 @@ export function refreshIds(data: NodeObj) {
|
|||
}
|
||||
|
||||
export const throttle = (fn: (any) => void, wait: number) => {
|
||||
var pre = Date.now();
|
||||
let pre = Date.now();
|
||||
return function () {
|
||||
var context = this;
|
||||
var args = arguments;
|
||||
var now = Date.now();
|
||||
const context = this;
|
||||
// eslint-disable-next-line prefer-rest-params
|
||||
const args = arguments;
|
||||
const now = Date.now();
|
||||
if (now - pre >= wait) {
|
||||
fn.apply(context, args);
|
||||
pre = Date.now();
|
||||
|
@ -78,7 +79,7 @@ export function getArrowPoints(p3x: number, p3y: number, p4x: number, p4y: numbe
|
|||
}
|
||||
const arrowLength = 20;
|
||||
const arrowAngle = 30;
|
||||
var a1 = angle + arrowAngle;
|
||||
const a1 = angle + arrowAngle;
|
||||
const a2 = angle - arrowAngle;
|
||||
return {
|
||||
x1: p4x + Math.cos((Math.PI * a1) / 180) * arrowLength,
|
||||
|
|
|
@ -11,19 +11,19 @@ Bus.prototype = {
|
|||
},
|
||||
fire: function (type, ...payload) {
|
||||
if (this.handlers[type] instanceof Array) {
|
||||
var handlers = this.handlers[type];
|
||||
for (var i = 0; i < handlers.length; i++) {
|
||||
const handlers = this.handlers[type];
|
||||
for (let i = 0; i < handlers.length; i++) {
|
||||
handlers[i](...payload);
|
||||
}
|
||||
}
|
||||
},
|
||||
removeListener: function (type, handler) {
|
||||
if (!this.handlers[type]) return;
|
||||
var handlers = this.handlers[type];
|
||||
const handlers = this.handlers[type];
|
||||
if (!handler) {
|
||||
handlers.length = 0;
|
||||
} else if (handlers.length) {
|
||||
for (var i = 0; i < handlers.length; i++) {
|
||||
for (let i = 0; i < handlers.length; i++) {
|
||||
if (handlers[i] === handler) {
|
||||
this.handlers[type].splice(i, 1);
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ export const StatusWrapper = ({ editor, node, updateAttributes }) => {
|
|||
updateAttributes({ defaultShowPicker: false });
|
||||
}
|
||||
},
|
||||
[defaultShowPicker, updateAttributes, createUser, user]
|
||||
[defaultShowPicker, toggleVisible, updateAttributes, createUser, user]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -80,7 +80,7 @@ export const StatusWrapper = ({ editor, node, updateAttributes }) => {
|
|||
toggleVisible(true);
|
||||
setTimeout(() => ref.current?.focus(), 100);
|
||||
}
|
||||
}, [defaultShowPicker, createUser, user]);
|
||||
}, [defaultShowPicker, toggleVisible, createUser, user]);
|
||||
|
||||
useEffect(() => {
|
||||
if (visible) {
|
||||
|
|
Loading…
Reference in New Issue