mirror of https://github.com/fantasticit/think.git
tiptap: add lazy render for flow and mind
This commit is contained in:
parent
86fc6bf843
commit
a03312b816
|
@ -90,6 +90,7 @@
|
||||||
"react-pdf": "^5.7.2",
|
"react-pdf": "^5.7.2",
|
||||||
"react-query": "^3.39.0",
|
"react-query": "^3.39.0",
|
||||||
"react-split-pane": "^0.1.92",
|
"react-split-pane": "^0.1.92",
|
||||||
|
"react-visibility-sensor": "^5.1.1",
|
||||||
"requestidlecallback-polyfill": "^1.0.2",
|
"requestidlecallback-polyfill": "^1.0.2",
|
||||||
"resize-observer-polyfill": "^1.5.1",
|
"resize-observer-polyfill": "^1.5.1",
|
||||||
"scroll-into-view-if-needed": "^2.2.29",
|
"scroll-into-view-if-needed": "^2.2.29",
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { IconFlow, IconMindCenter, IconZoomIn, IconZoomOut } from 'components/ic
|
||||||
import { Resizeable } from 'components/resizeable';
|
import { Resizeable } from 'components/resizeable';
|
||||||
import { useToggle } from 'hooks/use-toggle';
|
import { useToggle } from 'hooks/use-toggle';
|
||||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
|
import VisibilitySensor from 'react-visibility-sensor';
|
||||||
import { load, renderXml } from 'thirtypart/diagram';
|
import { load, renderXml } from 'thirtypart/diagram';
|
||||||
import { Flow } from 'tiptap/core/extensions/flow';
|
import { Flow } from 'tiptap/core/extensions/flow';
|
||||||
import { getEditorContainerDOMSize } from 'tiptap/prose-utils';
|
import { getEditorContainerDOMSize } from 'tiptap/prose-utils';
|
||||||
|
@ -22,6 +23,7 @@ export const FlowWrapper = ({ editor, node, updateAttributes }) => {
|
||||||
const $graph = useRef(null);
|
const $graph = useRef(null);
|
||||||
const $container = useRef<HTMLElement>();
|
const $container = useRef<HTMLElement>();
|
||||||
const [bgColor, setBgColor] = useState('var(--semi-color-bg-3)');
|
const [bgColor, setBgColor] = useState('var(--semi-color-bg-3)');
|
||||||
|
const [visible, toggleVisible] = useToggle(false);
|
||||||
const [loading, toggleLoading] = useToggle(true);
|
const [loading, toggleLoading] = useToggle(true);
|
||||||
const [error, setError] = useState(null);
|
const [error, setError] = useState(null);
|
||||||
|
|
||||||
|
@ -77,6 +79,15 @@ export const FlowWrapper = ({ editor, node, updateAttributes }) => {
|
||||||
[render]
|
[render]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const onViewportChange = useCallback(
|
||||||
|
(visible) => {
|
||||||
|
if (visible) {
|
||||||
|
toggleVisible(true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[toggleVisible]
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
load()
|
load()
|
||||||
.catch(setError)
|
.catch(setError)
|
||||||
|
@ -85,6 +96,7 @@ export const FlowWrapper = ({ editor, node, updateAttributes }) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NodeViewWrapper className={cls(styles.wrap, isActive && styles.isActive)}>
|
<NodeViewWrapper className={cls(styles.wrap, isActive && styles.isActive)}>
|
||||||
|
<VisibilitySensor onChange={onViewportChange}>
|
||||||
<Resizeable isEditable={isEditable} width={width} height={height} maxWidth={maxWidth} onChangeEnd={onResize}>
|
<Resizeable isEditable={isEditable} width={width} height={height} maxWidth={maxWidth} onChangeEnd={onResize}>
|
||||||
<div
|
<div
|
||||||
className={cls(styles.renderWrap, 'render-wrapper')}
|
className={cls(styles.renderWrap, 'render-wrapper')}
|
||||||
|
@ -98,8 +110,10 @@ export const FlowWrapper = ({ editor, node, updateAttributes }) => {
|
||||||
</Spin>
|
</Spin>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{error && <Text>{(error && error.message) || '未知错误'}</Text>}
|
{error && <Text>{(error && error.message) || '未知错误'}</Text>}
|
||||||
{!loading && !error && <div style={{ maxHeight: '100%' }} ref={setMxgraph}></div>}
|
|
||||||
|
{!loading && !error && visible && <div style={{ maxHeight: '100%' }} ref={setMxgraph}></div>}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.title}>
|
<div className={styles.title}>
|
||||||
|
@ -119,6 +133,7 @@ export const FlowWrapper = ({ editor, node, updateAttributes }) => {
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
</Resizeable>
|
</Resizeable>
|
||||||
|
</VisibilitySensor>
|
||||||
</NodeViewWrapper>
|
</NodeViewWrapper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { Tooltip } from 'components/tooltip';
|
||||||
import deepEqual from 'deep-equal';
|
import deepEqual from 'deep-equal';
|
||||||
import { useToggle } from 'hooks/use-toggle';
|
import { useToggle } from 'hooks/use-toggle';
|
||||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
|
import VisibilitySensor from 'react-visibility-sensor';
|
||||||
import { load, renderMind } from 'thirtypart/kityminder';
|
import { load, renderMind } from 'thirtypart/kityminder';
|
||||||
import { Mind } from 'tiptap/core/extensions/mind';
|
import { Mind } from 'tiptap/core/extensions/mind';
|
||||||
import { MAX_ZOOM, MIN_ZOOM, ZOOM_STEP } from 'tiptap/core/menus/mind/constant';
|
import { MAX_ZOOM, MIN_ZOOM, ZOOM_STEP } from 'tiptap/core/menus/mind/constant';
|
||||||
|
@ -24,6 +25,7 @@ export const MindWrapper = ({ editor, node, updateAttributes }) => {
|
||||||
const isActive = editor.isActive(Mind.name);
|
const isActive = editor.isActive(Mind.name);
|
||||||
const { width: maxWidth } = getEditorContainerDOMSize(editor);
|
const { width: maxWidth } = getEditorContainerDOMSize(editor);
|
||||||
const { data, width, height } = node.attrs;
|
const { data, width, height } = node.attrs;
|
||||||
|
const [visible, toggleVisible] = useToggle(false);
|
||||||
const [loading, toggleLoading] = useToggle(true);
|
const [loading, toggleLoading] = useToggle(true);
|
||||||
const [error, setError] = useState<Error | null>(null);
|
const [error, setError] = useState<Error | null>(null);
|
||||||
|
|
||||||
|
@ -76,6 +78,15 @@ export const MindWrapper = ({ editor, node, updateAttributes }) => {
|
||||||
[render]
|
[render]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const onViewportChange = useCallback(
|
||||||
|
(visible) => {
|
||||||
|
if (visible) {
|
||||||
|
toggleVisible(true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[toggleVisible]
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
load()
|
load()
|
||||||
.catch(setError)
|
.catch(setError)
|
||||||
|
@ -98,15 +109,21 @@ export const MindWrapper = ({ editor, node, updateAttributes }) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NodeViewWrapper className={cls(styles.wrap, isActive && styles.isActive)}>
|
<NodeViewWrapper className={cls(styles.wrap, isActive && styles.isActive)}>
|
||||||
|
<VisibilitySensor onChange={onViewportChange}>
|
||||||
<Resizeable isEditable={isEditable} width={width} height={height} maxWidth={maxWidth} onChangeEnd={onResize}>
|
<Resizeable isEditable={isEditable} width={width} height={height} maxWidth={maxWidth} onChangeEnd={onResize}>
|
||||||
<div className={cls(styles.renderWrap, 'render-wrapper')} style={{ ...INHERIT_SIZE_STYLE, overflow: 'hidden' }}>
|
<div
|
||||||
|
className={cls(styles.renderWrap, 'render-wrapper')}
|
||||||
|
style={{ ...INHERIT_SIZE_STYLE, overflow: 'hidden' }}
|
||||||
|
>
|
||||||
{error && (
|
{error && (
|
||||||
<div style={INHERIT_SIZE_STYLE}>
|
<div style={INHERIT_SIZE_STYLE}>
|
||||||
<Text>{error.message || error}</Text>
|
<Text>{error.message || error}</Text>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{loading && <Spin spinning style={INHERIT_SIZE_STYLE}></Spin>}
|
{loading && <Spin spinning style={INHERIT_SIZE_STYLE}></Spin>}
|
||||||
{!loading && !error && (
|
|
||||||
|
{!loading && !error && visible && (
|
||||||
<div style={{ height: '100%', maxHeight: '100%', overflow: 'hidden' }} ref={setMind}></div>
|
<div style={{ height: '100%', maxHeight: '100%', overflow: 'hidden' }} ref={setMind}></div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
@ -133,11 +150,18 @@ export const MindWrapper = ({ editor, node, updateAttributes }) => {
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip content="放大">
|
<Tooltip content="放大">
|
||||||
<Button size="small" theme="borderless" type="tertiary" icon={<IconZoomIn />} onClick={setZoom('plus')} />
|
<Button
|
||||||
|
size="small"
|
||||||
|
theme="borderless"
|
||||||
|
type="tertiary"
|
||||||
|
icon={<IconZoomIn />}
|
||||||
|
onClick={setZoom('plus')}
|
||||||
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Resizeable>
|
</Resizeable>
|
||||||
|
</VisibilitySensor>
|
||||||
</NodeViewWrapper>
|
</NodeViewWrapper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -135,6 +135,7 @@ importers:
|
||||||
react-pdf: ^5.7.2
|
react-pdf: ^5.7.2
|
||||||
react-query: ^3.39.0
|
react-query: ^3.39.0
|
||||||
react-split-pane: ^0.1.92
|
react-split-pane: ^0.1.92
|
||||||
|
react-visibility-sensor: ^5.1.1
|
||||||
requestidlecallback-polyfill: ^1.0.2
|
requestidlecallback-polyfill: ^1.0.2
|
||||||
resize-observer-polyfill: ^1.5.1
|
resize-observer-polyfill: ^1.5.1
|
||||||
scroll-into-view-if-needed: ^2.2.29
|
scroll-into-view-if-needed: ^2.2.29
|
||||||
|
@ -228,6 +229,7 @@ importers:
|
||||||
react-pdf: 5.7.2_react-dom@17.0.2+react@17.0.2
|
react-pdf: 5.7.2_react-dom@17.0.2+react@17.0.2
|
||||||
react-query: 3.39.0_react-dom@17.0.2+react@17.0.2
|
react-query: 3.39.0_react-dom@17.0.2+react@17.0.2
|
||||||
react-split-pane: 0.1.92_react-dom@17.0.2+react@17.0.2
|
react-split-pane: 0.1.92_react-dom@17.0.2+react@17.0.2
|
||||||
|
react-visibility-sensor: 5.1.1_react-dom@17.0.2+react@17.0.2
|
||||||
requestidlecallback-polyfill: 1.0.2
|
requestidlecallback-polyfill: 1.0.2
|
||||||
resize-observer-polyfill: 1.5.1
|
resize-observer-polyfill: 1.5.1
|
||||||
scroll-into-view-if-needed: 2.2.29
|
scroll-into-view-if-needed: 2.2.29
|
||||||
|
@ -246,7 +248,7 @@ importers:
|
||||||
eslint: 8.14.0
|
eslint: 8.14.0
|
||||||
eslint-config-prettier: 8.5.0_eslint@8.14.0
|
eslint-config-prettier: 8.5.0_eslint@8.14.0
|
||||||
eslint-plugin-import: 2.26.0_eslint@8.14.0
|
eslint-plugin-import: 2.26.0_eslint@8.14.0
|
||||||
eslint-plugin-prettier: 4.0.0_74ebb802163a9b4fa8f89d76ed02f62a
|
eslint-plugin-prettier: 4.0.0_740be41c8168d0cc214a306089357ad0
|
||||||
eslint-plugin-react: 7.29.4_eslint@8.14.0
|
eslint-plugin-react: 7.29.4_eslint@8.14.0
|
||||||
eslint-plugin-react-hooks: 4.5.0_eslint@8.14.0
|
eslint-plugin-react-hooks: 4.5.0_eslint@8.14.0
|
||||||
eslint-plugin-simple-import-sort: 7.0.0_eslint@8.14.0
|
eslint-plugin-simple-import-sort: 7.0.0_eslint@8.14.0
|
||||||
|
@ -5603,22 +5605,6 @@ packages:
|
||||||
prettier-linter-helpers: 1.0.0
|
prettier-linter-helpers: 1.0.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/eslint-plugin-prettier/4.0.0_74ebb802163a9b4fa8f89d76ed02f62a:
|
|
||||||
resolution: {integrity: sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ==}
|
|
||||||
engines: {node: '>=6.0.0'}
|
|
||||||
peerDependencies:
|
|
||||||
eslint: '>=7.28.0'
|
|
||||||
eslint-config-prettier: '*'
|
|
||||||
prettier: '>=2.0.0'
|
|
||||||
peerDependenciesMeta:
|
|
||||||
eslint-config-prettier:
|
|
||||||
optional: true
|
|
||||||
dependencies:
|
|
||||||
eslint: 8.14.0
|
|
||||||
eslint-config-prettier: 8.5.0_eslint@8.14.0
|
|
||||||
prettier-linter-helpers: 1.0.0
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/eslint-plugin-react-hooks/4.5.0_eslint@8.14.0:
|
/eslint-plugin-react-hooks/4.5.0_eslint@8.14.0:
|
||||||
resolution: {integrity: sha512-8k1gRt7D7h03kd+SAAlzXkQwWK22BnK6GKZG+FJA6BAGy22CFvl8kCIXKpVux0cCxMWDQUPqSok0LKaZ0aOcCw==}
|
resolution: {integrity: sha512-8k1gRt7D7h03kd+SAAlzXkQwWK22BnK6GKZG+FJA6BAGy22CFvl8kCIXKpVux0cCxMWDQUPqSok0LKaZ0aOcCw==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
@ -9611,6 +9597,17 @@ packages:
|
||||||
prop-types: 15.8.1
|
prop-types: 15.8.1
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/react-visibility-sensor/5.1.1_react-dom@17.0.2+react@17.0.2:
|
||||||
|
resolution: {integrity: sha512-cTUHqIK+zDYpeK19rzW6zF9YfT4486TIgizZW53wEZ+/GPBbK7cNS0EHyJVyHYacwFEvvHLEKfgJndbemWhB/w==}
|
||||||
|
peerDependencies:
|
||||||
|
react: '>=16.0.0'
|
||||||
|
react-dom: '>=16.0.0'
|
||||||
|
dependencies:
|
||||||
|
prop-types: 15.8.1
|
||||||
|
react: 17.0.2
|
||||||
|
react-dom: 17.0.2_react@17.0.2
|
||||||
|
dev: false
|
||||||
|
|
||||||
/react-window/1.8.6_react-dom@17.0.2+react@17.0.2:
|
/react-window/1.8.6_react-dom@17.0.2+react@17.0.2:
|
||||||
resolution: {integrity: sha512-8VwEEYyjz6DCnGBsd+MgkD0KJ2/OXFULyDtorIiTz+QzwoP94tBoA7CnbtyXMm+cCeAUER5KJcPtWl9cpKbOBg==}
|
resolution: {integrity: sha512-8VwEEYyjz6DCnGBsd+MgkD0KJ2/OXFULyDtorIiTz+QzwoP94tBoA7CnbtyXMm+cCeAUER5KJcPtWl9cpKbOBg==}
|
||||||
engines: {node: '>8.0.0'}
|
engines: {node: '>8.0.0'}
|
||||||
|
|
Loading…
Reference in New Issue