tiptap: fix mind

This commit is contained in:
fantasticit 2022-08-19 18:20:56 +08:00
parent 3b68238249
commit acd266acc0
9 changed files with 112 additions and 28 deletions

View File

@ -24,15 +24,25 @@ export function renderMind(options: Options) {
const Editor = window.kityminder.Editor; const Editor = window.kityminder.Editor;
const editor = new Editor(options.container); const editor = new Editor(options.container);
const mind = editor.minder; const mind = editor.minder;
mind.editor = editor;
options.data && mind.importJson(options.data); options.data && mind.importJson(options.data);
if (!options.isEditable) { if (!options.isEditable) {
mind.disable(); mind.disable();
mind.setStatus('readonly');
} }
setTimeout(() => { setTimeout(() => {
mind.execCommand('camera'); mind.execCommand('camera');
if (!options.isEditable) {
const selectedNodes = mind.getSelectedNodes();
if (selectedNodes.length) {
mind.removeSelectedNodes(selectedNodes);
}
}
}, 0); }, 0);
return mind; return mind;

View File

@ -138,6 +138,9 @@ define(function (require, exports, module) {
}, },
_bindEvents: function () { _bindEvents: function () {
if (this.getStatus() === 'readonly') {
return this;
}
/* jscs:disable maximumLineLength */ /* jscs:disable maximumLineLength */
this._paper.on( this._paper.on(
'click dblclick mousedown contextmenu mouseup mousemove mouseover mousewheel DOMMouseScroll touchstart touchmove touchend dragenter dragleave drop', 'click dblclick mousedown contextmenu mouseup mousemove mouseover mousewheel DOMMouseScroll touchstart touchmove touchend dragenter dragleave drop',

View File

@ -17,6 +17,10 @@ define(function (require, exports, module) {
kity.extendClass(Minder, { kity.extendClass(Minder, {
focus: function () { focus: function () {
if (this.getStatus() === 'readonly') {
return this;
}
if (!this.isFocused()) { if (!this.isFocused()) {
var renderTarget = this._renderTarget; var renderTarget = this._renderTarget;
renderTarget.classList.add('focus'); renderTarget.classList.add('focus');

View File

@ -23,6 +23,10 @@ define(function (require, exports, module) {
kity.extendClass(Minder, { kity.extendClass(Minder, {
_initKeyReceiver: function () { _initKeyReceiver: function () {
if (this.getStatus() === 'readonly') {
return;
}
if (this._keyReceiver) return; if (this._keyReceiver) return;
var receiver = (this._keyReceiver = document.createElement('input')); var receiver = (this._keyReceiver = document.createElement('input'));
@ -34,6 +38,10 @@ define(function (require, exports, module) {
var minder = this; var minder = this;
listen(receiver, 'keydown keyup keypress copy paste blur focus input', function (e) { listen(receiver, 'keydown keyup keypress copy paste blur focus input', function (e) {
if (minder.getStatus() === 'readonly') {
return;
}
switch (e.type) { switch (e.type) {
case 'blur': case 'blur':
minder.blur(); minder.blur();
@ -50,14 +58,24 @@ define(function (require, exports, module) {
}); });
this.on('focus', function () { this.on('focus', function () {
if (this.getStatus() === 'readonly') {
return;
}
receiver.select(); receiver.select();
receiver.focus(); receiver.focus();
}); });
this.on('blur', function () { this.on('blur', function () {
if (this.getStatus() === 'readonly') {
return;
}
receiver.blur(); receiver.blur();
}); });
if (this.isFocused()) { if (this.isFocused()) {
if (this.getStatus() === 'readonly') {
return;
}
receiver.select(); receiver.select();
receiver.focus(); receiver.focus();
} }

View File

@ -142,6 +142,8 @@ define(function (require, exports, module) {
initEvent: function (node) { initEvent: function (node) {
this.on('mousedown', function (e) { this.on('mousedown', function (e) {
if (minder._status === 'readonly') return;
minder.select([node], true); minder.select([node], true);
if (node.isExpanded()) { if (node.isExpanded()) {
node.collapse(); node.collapse();

View File

@ -21,9 +21,11 @@ define(function (require, exports, module) {
var receiverElement = receiver.element; var receiverElement = receiver.element;
var isGecko = window.kity.Browser.gecko; var isGecko = window.kity.Browser.gecko;
// setup everything to go // setup everything to go
setupReciverElement(); setupReciverElement();
setupFsm(); setupFsm();
setupHotbox(); setupHotbox();
// expose editText() // expose editText()
this.editText = editText; this.editText = editText;
// listen the fsm changes, make action. // listen the fsm changes, make action.
@ -32,6 +34,8 @@ define(function (require, exports, module) {
fsm.when('* -> input', enterInputMode); fsm.when('* -> input', enterInputMode);
// when exited, commit or exit depends on the exit reason // when exited, commit or exit depends on the exit reason
fsm.when('input -> *', function (exit, enter, reason) { fsm.when('input -> *', function (exit, enter, reason) {
if (minder._status === 'readonly') return;
switch (reason) { switch (reason) {
case 'input-cancel': case 'input-cancel':
return exitInputMode(); return exitInputMode();
@ -43,11 +47,15 @@ define(function (require, exports, module) {
}); });
// lost focus to commit // lost focus to commit
receiver.onblur(function (e) { receiver.onblur(function (e) {
if (minder._status === 'readonly') return;
if (fsm.state() == 'input') { if (fsm.state() == 'input') {
fsm.jump('normal', 'input-commit'); fsm.jump('normal', 'input-commit');
} }
}); });
minder.on('beforemousedown', function () { minder.on('beforemousedown', function () {
if (minder._status === 'readonly') return;
if (fsm.state() == 'input') { if (fsm.state() == 'input') {
fsm.jump('normal', 'input-commit'); fsm.jump('normal', 'input-commit');
} }
@ -69,10 +77,15 @@ define(function (require, exports, module) {
minder.on('layoutallfinish viewchange viewchanged selectionchange', function (e) { minder.on('layoutallfinish viewchange viewchanged selectionchange', function (e) {
// viewchange event is too frequenced, lazy it // viewchange event is too frequenced, lazy it
if (e.type == 'viewchange' && fsm.state() != 'input') return; if (e.type == 'viewchange' && fsm.state() != 'input') return;
if (minder.getStatus() !== 'readonly') {
updatePosition(); updatePosition();
}
}); });
if (minder.getStatus() !== 'readonly') {
updatePosition(); updatePosition();
} }
}
// edit entrance in hotbox // edit entrance in hotbox
function setupHotbox() { function setupHotbox() {
hotbox.state('main').button({ hotbox.state('main').button({
@ -121,6 +134,8 @@ define(function (require, exports, module) {
* @Date 2015-12-2 * @Date 2015-12-2
*/ */
function enterInputMode() { function enterInputMode() {
if (minder._status === 'readonly') return;
var node = minder.getSelectedNode(); var node = minder.getSelectedNode();
if (node) { if (node) {
var fontSize = node.getData('font-size') || node.getStyle('font-size'); var fontSize = node.getData('font-size') || node.getStyle('font-size');
@ -141,6 +156,13 @@ define(function (require, exports, module) {
* @Date: 2015.9.16 * @Date: 2015.9.16
*/ */
function commitInputText(textNodes) { function commitInputText(textNodes) {
if (minder._status === 'readonly') return;
var node = minder.getSelectedNode();
if (!node) {
return;
}
var text = ''; var text = '';
var TAB_CHAR = '\t', var TAB_CHAR = '\t',
ENTER_CHAR = '\n', ENTER_CHAR = '\n',
@ -282,6 +304,8 @@ define(function (require, exports, module) {
* @Date: 2015.9.16 * @Date: 2015.9.16
*/ */
function commitInputNode(node, text) { function commitInputNode(node, text) {
if (minder._status === 'readonly') return;
try { try {
minder.decodeData('text', text).then(function (json) { minder.decodeData('text', text).then(function (json) {
function importText(node, json, minder) { function importText(node, json, minder) {
@ -309,6 +333,8 @@ define(function (require, exports, module) {
} }
} }
function commitInputResult() { function commitInputResult() {
if (minder._status === 'readonly') return;
/** /**
* @Desc: * @Desc:
* *
@ -338,10 +364,14 @@ define(function (require, exports, module) {
} }
} }
function exitInputMode() { function exitInputMode() {
if (minder._status === 'readonly') return;
receiverElement.classList.remove('input'); receiverElement.classList.remove('input');
receiver.selectAll(); receiver.selectAll();
} }
function updatePosition() { function updatePosition() {
if (minder._status === 'readonly') return;
var planed = updatePosition; var planed = updatePosition;
var focusNode = minder.getSelectedNode(); var focusNode = minder.getSelectedNode();
if (!focusNode) return; if (!focusNode) return;

View File

@ -48,18 +48,26 @@ define(function (require, exports, module) {
var receiverElement = receiver.element; var receiverElement = receiver.element;
var hotbox = this.hotbox; var hotbox = this.hotbox;
var compositionLock = false; var compositionLock = false;
var that = this;
// normal -> * // normal -> *
receiver.listen('normal', function (e) { receiver.listen('normal', function (e) {
if (minder._status === 'readonly') return;
// 为了防止处理进入edit模式而丢失处理的首字母,此时receiver必须为enable // 为了防止处理进入edit模式而丢失处理的首字母,此时receiver必须为enable
receiver.enable(); receiver.enable();
// normal -> hotbox // normal -> hotbox
if (e.is('Space')) { if (e.is('Space')) {
// 非编辑模式
if (that.minder.getStatus() === 'readonly') {
return;
}
e.preventDefault(); e.preventDefault();
// safari下Space触发hotbox,然而这时Space已在receiver上留下作案痕迹,因此抹掉 // safari下Space触发hotbox,然而这时Space已在receiver上留下作案痕迹,因此抹掉
if (kity.Browser.safari) { if (kity.Browser.safari) {
receiverElement.innerHTML = ''; receiverElement.innerHTML = '';
} }
return fsm.jump('hotbox', 'space-trigger'); return fsm.jump('hotbox', 'space-trigger');
} }
@ -91,6 +99,8 @@ define(function (require, exports, module) {
// hotbox -> normal // hotbox -> normal
receiver.listen('hotbox', function (e) { receiver.listen('hotbox', function (e) {
if (minder._status === 'readonly') return;
receiver.disable(); receiver.disable();
e.preventDefault(); e.preventDefault();
var handleResult = hotbox.dispatch(e); var handleResult = hotbox.dispatch(e);
@ -101,6 +111,8 @@ define(function (require, exports, module) {
// input => normal // input => normal
receiver.listen('input', function (e) { receiver.listen('input', function (e) {
if (minder._status === 'readonly') return;
receiver.enable(); receiver.enable();
if (e.type == 'keydown') { if (e.type == 'keydown') {
if (e.is('Enter')) { if (e.is('Enter')) {
@ -138,6 +150,8 @@ define(function (require, exports, module) {
container.addEventListener( container.addEventListener(
'mousedown', 'mousedown',
function (e) { function (e) {
if (minder._status === 'readonly') return;
if (e.button == MOUSE_RB) { if (e.button == MOUSE_RB) {
e.preventDefault(); e.preventDefault();
} }
@ -155,6 +169,8 @@ define(function (require, exports, module) {
container.addEventListener( container.addEventListener(
'mousewheel', 'mousewheel',
function (e) { function (e) {
if (minder._status === 'readonly') return;
if (fsm.state() == 'hotbox') { if (fsm.state() == 'hotbox') {
hotbox.active(Hotbox.STATE_IDLE); hotbox.active(Hotbox.STATE_IDLE);
fsm.jump('normal', 'mousemove-blur'); fsm.jump('normal', 'mousemove-blur');
@ -164,12 +180,14 @@ define(function (require, exports, module) {
); );
container.addEventListener('contextmenu', function (e) { container.addEventListener('contextmenu', function (e) {
if (minder._status === 'readonly') return;
e.preventDefault(); e.preventDefault();
}); });
container.addEventListener( container.addEventListener(
'mouseup', 'mouseup',
function (e) { function (e) {
if (minder._status === 'readonly') return;
if (fsm.state() != 'normal') { if (fsm.state() != 'normal') {
return; return;
} }
@ -186,6 +204,7 @@ define(function (require, exports, module) {
// 阻止热盒事件冒泡,在热盒正确执行前导致热盒关闭 // 阻止热盒事件冒泡,在热盒正确执行前导致热盒关闭
hotbox.$element.addEventListener('mousedown', function (e) { hotbox.$element.addEventListener('mousedown', function (e) {
if (minder._status === 'readonly') return;
e.stopPropagation(); e.stopPropagation();
}); });
} }

View File

@ -46,7 +46,14 @@ export const MindSettingModal: React.FC<IProps> = ({ editor }) => {
return; return;
} }
const data = mind.exportJson(); const data = mind.exportJson();
/**
* FIXME: 百度脑图更新后会滚动 dom hack
*/
const currentScrollTop = document.querySelector('main#js-tocs-container')?.scrollTop;
editor.chain().focus().setMind({ data }).run(); editor.chain().focus().setMind({ data }).run();
setTimeout(() => {
document.querySelector('main#js-tocs-container').scrollTop = currentScrollTop;
});
toggleVisible(false); toggleVisible(false);
}, [editor, toggleVisible, mind]); }, [editor, toggleVisible, mind]);

View File

@ -20,6 +20,7 @@ const { Text } = Typography;
const INHERIT_SIZE_STYLE = { width: '100%', height: '100%', maxWidth: '100%' }; const INHERIT_SIZE_STYLE = { width: '100%', height: '100%', maxWidth: '100%' };
export const MindWrapper = ({ editor, node, updateAttributes }) => { export const MindWrapper = ({ editor, node, updateAttributes }) => {
const $div = useRef(null);
const $mind = useRef(null); const $mind = useRef(null);
const isEditable = editor.isEditable; const isEditable = editor.isEditable;
const isActive = editor.isActive(Mind.name); const isActive = editor.isActive(Mind.name);
@ -32,7 +33,7 @@ export const MindWrapper = ({ editor, node, updateAttributes }) => {
const setCenter = useCallback(() => { const setCenter = useCallback(() => {
const mind = $mind.current; const mind = $mind.current;
if (!mind) return; if (!mind) return;
mind.execCommand('camera'); mind.execCommand('camera', mind.getRoot(), 600);
}, []); }, []);
const setZoom = useCallback((type: 'minus' | 'plus') => { const setZoom = useCallback((type: 'minus' | 'plus') => {
@ -55,29 +56,6 @@ export const MindWrapper = ({ editor, node, updateAttributes }) => {
[updateAttributes, setCenter] [updateAttributes, setCenter]
); );
const render = useCallback(
(div) => {
if (!div) return;
if (!$mind.current) {
const graph = renderMind({
container: div,
data,
isEditable: false,
});
$mind.current = graph;
}
},
[data]
);
const setMind = useCallback(
(div) => {
render(div);
},
[render]
);
const onViewportChange = useCallback( const onViewportChange = useCallback(
(visible) => { (visible) => {
if (visible) { if (visible) {
@ -107,6 +85,19 @@ export const MindWrapper = ({ editor, node, updateAttributes }) => {
setCenter(); setCenter();
}, [width, height, setCenter]); }, [width, height, setCenter]);
useEffect(() => {
if (visible && !loading && !error) {
if (!$mind.current) {
const graph = renderMind({
container: $div.current,
data,
isEditable: false,
});
$mind.current = graph;
}
}
}, [visible, data, loading, error]);
return ( return (
<NodeViewWrapper className={cls(styles.wrap, isActive && styles.isActive)}> <NodeViewWrapper className={cls(styles.wrap, isActive && styles.isActive)}>
<VisibilitySensor onChange={onViewportChange}> <VisibilitySensor onChange={onViewportChange}>
@ -123,8 +114,8 @@ export const MindWrapper = ({ editor, node, updateAttributes }) => {
{loading && <Spin spinning style={INHERIT_SIZE_STYLE}></Spin>} {loading && <Spin spinning style={INHERIT_SIZE_STYLE}></Spin>}
{!loading && !error && visible && ( {!loading && !error && (
<div style={{ height: '100%', maxHeight: '100%', overflow: 'hidden' }} ref={setMind}></div> <div style={{ height: '100%', maxHeight: '100%', overflow: 'hidden' }} ref={$div}></div>
)} )}
<div className={styles.title}> <div className={styles.title}>