diff --git a/packages/client/src/tiptap/divider.tsx b/packages/client/src/tiptap/divider.tsx index eb451e75..8f44ee33 100644 --- a/packages/client/src/tiptap/divider.tsx +++ b/packages/client/src/tiptap/divider.tsx @@ -4,7 +4,7 @@ export const Divider = ({ vertical = false }) => { style={{ display: 'inline-block', width: 1, - height: 24, + height: 18, margin: '0 6px', backgroundColor: 'var(--semi-color-border)', transform: `rotate(${vertical ? 90 : 0}deg)`, diff --git a/packages/client/src/tiptap/styles/mind/toolbar.scss b/packages/client/src/tiptap/styles/mind/toolbar.scss index 96f62812..b41b94c3 100644 --- a/packages/client/src/tiptap/styles/mind/toolbar.scss +++ b/packages/client/src/tiptap/styles/mind/toolbar.scss @@ -2,7 +2,7 @@ .toolbar { position: absolute; display: flex; - padding: 8px; + padding: 4px 8px; overflow-x: auto; color: #fff; background-color: var(--semi-color-nav-bg); diff --git a/packages/client/src/tiptap/wrappers/mind/index.tsx b/packages/client/src/tiptap/wrappers/mind/index.tsx index 8e239db2..16df2e67 100644 --- a/packages/client/src/tiptap/wrappers/mind/index.tsx +++ b/packages/client/src/tiptap/wrappers/mind/index.tsx @@ -85,11 +85,11 @@ export const MindWrapper = ({ editor, node, updateAttributes }) => { direction: window.MindElixir.SIDE, data: JSON.parse(JSON.stringify(data)), editable: editor.isEditable, - draggable: editor.isEditable, contextMenu: editor.isEditable, - toolBar: true, keypress: editor.isEditable, - nodeMenu: true, + nodeMenu: editor.isEditable, + toolBar: true, + draggable: false, // 需要修复 locale: 'zh_CN', }); mind.shouldPreventDefault = () => editor.isActive('mind'); @@ -103,7 +103,7 @@ export const MindWrapper = ({ editor, node, updateAttributes }) => { return () => { if (mind) { - mind.bus.removeListener('operation', onChange); + mind.destroy(); } }; }, [loading, editor, updateAttributes]); diff --git a/packages/client/src/tiptap/wrappers/mind/mind-elixir/customLink.ts b/packages/client/src/tiptap/wrappers/mind/mind-elixir/customLink.ts index 34afd5f9..9416de63 100644 --- a/packages/client/src/tiptap/wrappers/mind/mind-elixir/customLink.ts +++ b/packages/client/src/tiptap/wrappers/mind/mind-elixir/customLink.ts @@ -112,6 +112,11 @@ export const createLink = function (from, to, isInitPaint, obj) { if (!isInitPaint) { this.showLinkController(p2x, p2y, p3x, p3y, newLinkObj, fromData, toData); } + + this.bus.fire('operation', { + name: 'createLink', + linkObj: newLinkObj, + }); }; export const removeLink = function (linkSvg) { @@ -129,6 +134,10 @@ export const removeLink = function (linkSvg) { delete this.linkData[id]; link.remove(); link = null; + + this.bus.fire('operation', { + name: 'removeLink', + }); }; export const selectLink = function (targetElement) { this.currentLink = targetElement; @@ -222,6 +231,11 @@ export const showLinkController = function (p2x, p2y, p3x, p3y, linkObj, fromDat this.line1.setAttribute('y2', p2y); linkObj.delta1.x = p2x - fromData.cx; linkObj.delta1.y = p2y - fromData.cy; + + this.bus.fire('operation', { + name: 'updateLink', + linkObj, + }); }); this.helper2.init(this.map, (deltaX, deltaY) => { @@ -246,5 +260,10 @@ export const showLinkController = function (p2x, p2y, p3x, p3y, linkObj, fromDat this.line2.setAttribute('y2', p4y); linkObj.delta2.x = p3x - toData.cx; linkObj.delta2.y = p3y - toData.cy; + + this.bus.fire('operation', { + name: 'updateLink', + linkObj, + }); }); }; diff --git a/packages/client/src/tiptap/wrappers/mind/mind-elixir/i18n.ts b/packages/client/src/tiptap/wrappers/mind/mind-elixir/i18n.ts index 92766f95..9df9c67c 100644 --- a/packages/client/src/tiptap/wrappers/mind/mind-elixir/i18n.ts +++ b/packages/client/src/tiptap/wrappers/mind/mind-elixir/i18n.ts @@ -20,80 +20,4 @@ const cn = { export default { cn, zh_CN: cn, - zh_TW: { - addChild: '插入子節點', - addParent: '插入父節點', - addSibling: '插入同級節點', - removeNode: '刪除節點', - focus: '專注', - cancelFocus: '取消專注', - moveUp: '上移', - moveDown: '下移', - link: '連接', - clickTips: '請點擊目標節點', - - font: '文字', - background: '背景', - tag: '標簽', - icon: '圖標', - tagsSeparate: '多個標簽半角逗號分隔', - iconsSeparate: '多個圖標半角逗號分隔', - }, - en: { - addChild: 'Add child', - addParent: 'Add parent', - addSibling: 'Add sibling', - removeNode: 'Remove node', - focus: 'Focus Mode', - cancelFocus: 'Cancel Focus Mode', - moveUp: 'Move up', - moveDown: 'Move down', - link: 'Link', - clickTips: 'Please click the target node', - - font: 'Font', - background: 'Background', - tag: 'Tag', - icon: 'Icon', - tagsSeparate: 'Separate tags by comma', - iconsSeparate: 'Separate icons by comma', - }, - ja: { - addChild: '子ノードを追加する', - addParent: '親ノードを追加します', - addSibling: '兄弟ノードを追加する', - removeNode: 'ノードを削除', - focus: '集中', - cancelFocus: '集中解除', - moveUp: '上へ移動', - moveDown: '下へ移動', - link: 'コネクト', - clickTips: 'ターゲットノードをクリックしてください', - - font: 'フォント', - background: 'バックグラウンド', - tag: 'タグ', - icon: 'アイコン', - tagsSeparate: '複数タグはカンマ区切り', - iconsSeparate: '複数アイコンはカンマ区切り', - }, - pt: { - addChild: 'Adicionar item filho', - addParent: 'Adicionar item pai', - addSibling: 'Adicionar item irmao', - removeNode: 'Remover item', - focus: 'Modo Foco', - cancelFocus: 'Cancelar Modo Foco', - moveUp: 'Mover para cima', - moveDown: 'Mover para baixo', - link: 'Link', - clickTips: 'Favor clicar no item alvo', - - font: 'Fonte', - background: 'Cor de fundo', - tag: 'Tag', - icon: 'Icone', - tagsSeparate: 'Separe tags por virgula', - iconsSeparate: 'Separe icones por virgula', - }, }; diff --git a/packages/client/src/tiptap/wrappers/mind/mind-elixir/index.ts b/packages/client/src/tiptap/wrappers/mind/mind-elixir/index.ts index 0e680455..34ab804f 100644 --- a/packages/client/src/tiptap/wrappers/mind/mind-elixir/index.ts +++ b/packages/client/src/tiptap/wrappers/mind/mind-elixir/index.ts @@ -48,6 +48,7 @@ import { updateNodeStyle, updateNodeTags, updateNodeIcons, + updateNodeHyperLink, processPrimaryNode, setNodeTopic, moveNodeBefore, @@ -294,6 +295,7 @@ MindElixir.prototype = { updateNodeStyle, updateNodeTags, updateNodeIcons, + updateNodeHyperLink, processPrimaryNode, setNodeTopic, @@ -400,6 +402,11 @@ MindElixir.prototype = { this.linkDiv(); if (!this.overflowHidden) initMouseEvent(this); }, + + destroy: function () { + this.bus.destroy(); + this.mindElixirBox.removeChild(this.container); + }, }; MindElixir.LEFT = LEFT; diff --git a/packages/client/src/tiptap/wrappers/mind/mind-elixir/interact.ts b/packages/client/src/tiptap/wrappers/mind/mind-elixir/interact.ts index bf888054..5dd3ab4d 100644 --- a/packages/client/src/tiptap/wrappers/mind/mind-elixir/interact.ts +++ b/packages/client/src/tiptap/wrappers/mind/mind-elixir/interact.ts @@ -28,6 +28,7 @@ export const selectNode = function (targetElement, isNewNode, clickEvent) { targetElement.classList.add('selected'); this.currentNode = targetElement; if (isNewNode) { + console.log('selectNewNode 1'); this.bus.fire('selectNewNode', targetElement.nodeObj, clickEvent); } else { this.bus.fire('selectNode', targetElement.nodeObj, clickEvent); diff --git a/packages/client/src/tiptap/wrappers/mind/mind-elixir/mouse.ts b/packages/client/src/tiptap/wrappers/mind/mind-elixir/mouse.ts index 447b01df..3bee4152 100644 --- a/packages/client/src/tiptap/wrappers/mind/mind-elixir/mouse.ts +++ b/packages/client/src/tiptap/wrappers/mind/mind-elixir/mouse.ts @@ -1,6 +1,7 @@ import { dragMoveHelper } from './utils/index'; + export default function (mind) { - mind.map.addEventListener('click', (e) => { + const onClick = (e) => { // if (dragMoveHelper.afterMoving) return // e.preventDefault() // can cause a tag don't work if (e.target.nodeName === 'EPD') { @@ -17,36 +18,52 @@ export default function (mind) { mind.unselectNode(); mind.hideLinkController(); } - }); + }; - mind.map.addEventListener('dblclick', (e) => { + const onDbClick = (e) => { e.preventDefault(); if (!mind.editable) return; if (e.target.parentElement.nodeName === 'T' || e.target.parentElement.nodeName === 'ROOT') { mind.beginEdit(e.target); } - }); + }; - /** - * drag and move - */ - mind.map.addEventListener('mousemove', (e) => { + const onMouseMove = (e) => { // click trigger mousemove in windows chrome // the 'true' is a string if (e.target.contentEditable !== 'true') { dragMoveHelper.onMove(e, mind.container); } - }); - mind.map.addEventListener('mousedown', (e) => { + }; + + const onMouseDown = (e) => { if (e.target.contentEditable !== 'true') { dragMoveHelper.afterMoving = false; dragMoveHelper.mousedown = true; } - }); - mind.map.addEventListener('mouseleave', (e) => { + }; + + const onMouseLeave = (e) => { dragMoveHelper.clear(); - }); - mind.map.addEventListener('mouseup', (e) => { + }; + + const onMouseUp = (e) => { dragMoveHelper.clear(); + }; + + mind.map.addEventListener('click', onClick); + mind.map.addEventListener('dblclick', onDbClick); + mind.map.addEventListener('mousemove', onMouseMove); + mind.map.addEventListener('mousedown', onMouseDown); + mind.map.addEventListener('mouseleave', onMouseLeave); + mind.map.addEventListener('mouseup', onMouseUp); + + mind.bus.addListener('destroy', () => { + mind.map.removeEventListener('click', onClick); + mind.map.removeEventListener('dblclick', onDbClick); + mind.map.removeEventListener('mousemove', onMouseMove); + mind.map.removeEventListener('mousedown', onMouseDown); + mind.map.removeEventListener('mouseleave', onMouseLeave); + mind.map.removeEventListener('mouseup', onMouseUp); }); } diff --git a/packages/client/src/tiptap/wrappers/mind/mind-elixir/nodeOperation.ts b/packages/client/src/tiptap/wrappers/mind/mind-elixir/nodeOperation.ts index b1e90a5d..8affcc76 100644 --- a/packages/client/src/tiptap/wrappers/mind/mind-elixir/nodeOperation.ts +++ b/packages/client/src/tiptap/wrappers/mind/mind-elixir/nodeOperation.ts @@ -73,17 +73,24 @@ export const updateNodeIcons = function (object, icons) { }; export const updateNodeHyperLink = function (object, hyperLink) { - if (!hyperLink) return; - const oldVal = object.hyperLink; - object.hyperLink = hyperLink; const nodeEle = findEle(object.id); - shapeTpc(nodeEle, object); - this.linkDiv(); - this.bus.fire('operation', { - name: 'editHyperLink', - obj: object, - origin: oldVal, - }); + + if (!hyperLink) { + const linkEle = nodeEle.querySelector('a.hyper-link') as HTMLElement; + if (linkEle) { + linkEle.parentNode.removeChild(linkEle); + } + } else { + const oldVal = object.hyperLink; + object.hyperLink = hyperLink; + shapeTpc(nodeEle, object); + this.linkDiv(); + this.bus.fire('operation', { + name: 'editHyperLink', + obj: object, + origin: oldVal, + }); + } }; export const updateNodeSvgChart = function () { @@ -404,6 +411,7 @@ export const removeNode = function (el) { if (t.tagName === 'ROOT') { return; } + this.bus.fire('removeNode', nodeObj); if (childrenLength === 0) { // remove epd when children length === 0 const parentT = t.parentNode.parentNode.previousSibling; diff --git a/packages/client/src/tiptap/wrappers/mind/mind-elixir/plugin/keypress.ts b/packages/client/src/tiptap/wrappers/mind/mind-elixir/plugin/keypress.ts index 05f4f372..edd04862 100644 --- a/packages/client/src/tiptap/wrappers/mind/mind-elixir/plugin/keypress.ts +++ b/packages/client/src/tiptap/wrappers/mind/mind-elixir/plugin/keypress.ts @@ -82,6 +82,7 @@ export default function (mind) { }; document.addEventListener('keydown', (e) => { + if (mind.isInnerEditing) return; // 脑图内部操作 if (!mind.editable) return; if (mind.shouldPreventDefault && mind.shouldPreventDefault()) { diff --git a/packages/client/src/tiptap/wrappers/mind/mind-elixir/plugin/mobileMenu.ts b/packages/client/src/tiptap/wrappers/mind/mind-elixir/plugin/mobileMenu.ts index 45529921..f29181c5 100644 --- a/packages/client/src/tiptap/wrappers/mind/mind-elixir/plugin/mobileMenu.ts +++ b/packages/client/src/tiptap/wrappers/mind/mind-elixir/plugin/mobileMenu.ts @@ -11,18 +11,13 @@ export default function (mind, option?) { const add_child = createLi('cm-add_child', 'zijiedian'); const add_sibling = createLi('cm-add_sibling', 'tongjijiedian-'); const remove_child = createLi('cm-remove_child', 'shanchu2'); - // let focus = createLi('cm-fucus', i18n[locale].focus, '') - // let unfocus = createLi('cm-unfucus', i18n[locale].cancelFocus, '') const up = createLi('cm-up', 'rising'); const down = createLi('cm-down', 'falling'); const edit = createLi('cm-edit', 'edit'); - // let link = createLi('cm-down', i18n[locale].link, '') const menuUl = document.createElement('ul'); menuUl.className = 'menu-list'; - // if (!option || option.link) { - // menuUl.appendChild(link) - // } + if (option && option.extend) { for (let i = 0; i < option.extend.length; i++) { const item = option.extend[i]; @@ -37,10 +32,6 @@ export default function (mind, option?) { menuContainer.appendChild(add_child); menuContainer.appendChild(add_sibling); menuContainer.appendChild(remove_child); - // if (!option || option.focus) { - // menuContainer.appendChild(focus) - // menuContainer.appendChild(unfocus) - // } menuContainer.appendChild(up); menuContainer.appendChild(down); menuContainer.appendChild(edit); @@ -48,39 +39,6 @@ export default function (mind, option?) { mind.container.append(menuContainer); let isRoot = true; - // mind.container.onclick = function (e) { - // e.preventDefault() - // // console.log(e.pageY, e.screenY, e.clientY) - // let target = e.target - // if (target.tagName === 'TPC') { - // if (target.parentElement.tagName === 'ROOT') { - // isRoot = true - // } else { - // isRoot = false - // } - // // if (isRoot) { - // // focus.className = 'disabled' - // // up.className = 'disabled' - // // down.className = 'disabled' - // // add_sibling.className = 'disabled' - // // remove_child.className = 'disabled' - // // } else { - // // focus.className = '' - // // up.className = '' - // // down.className = '' - // // add_sibling.className = '' - // // remove_child.className = '' - // // } - // mind.selectNode(target) - // menuContainer.hidden = false - // let height = menuUl.offsetHeight - // let width = menuUl.offsetWidth - // let rect = target.getBoundingClientRect() - // // menuUl.style.top = rect.top - 10 - height + 'px' - // // menuUl.style.left = rect.left - (width - rect.width) / 2 + 'px' - // // menuUl.style.left = e.clientX + 'px' - // } - // } mind.bus.addListener('unselectNode', function () { menuContainer.hidden = true; @@ -108,15 +66,6 @@ export default function (mind, option?) { if (isRoot) return; mind.removeNode(); }; - // focus.onclick = e => { - // if (isRoot) return - // mind.focusNode(mind.currentNode) - // menuContainer.hidden = true - // } - // unfocus.onclick = e => { - // mind.cancelFocus() - // menuContainer.hidden = true - // } up.onclick = (e) => { if (isRoot) return; mind.moveUpNode(); @@ -128,24 +77,4 @@ export default function (mind, option?) { edit.onclick = (e) => { mind.beginEdit(); }; - // link.onclick = e => { - // let from = mind.currentNode - // mind.map.addEventListener( - // 'click', - // e => { - // e.preventDefault() - // if ( - // e.target.parentElement.nodeName === 'T' || - // e.target.parentElement.nodeName === 'ROOT' - // ) { - // mind.createLink(from, mind.currentNode) - // } else { - // console.log('取消连接') - // } - // }, - // { - // once: true, - // } - // ) - // } } diff --git a/packages/client/src/tiptap/wrappers/mind/mind-elixir/plugin/nodeDraggable.ts b/packages/client/src/tiptap/wrappers/mind/mind-elixir/plugin/nodeDraggable.ts index 8f28ca01..ca3abd90 100644 --- a/packages/client/src/tiptap/wrappers/mind/mind-elixir/plugin/nodeDraggable.ts +++ b/packages/client/src/tiptap/wrappers/mind/mind-elixir/plugin/nodeDraggable.ts @@ -1,8 +1,8 @@ import { dragMoveHelper, throttle } from '../utils/index'; import { findEle as E, Topic, Group } from '../utils/dom'; -// https://html.spec.whatwg.org/multipage/dnd.html#drag-and-drop-processing-model const $d = document; + const insertPreview = function (el, insertLocation) { if (!insertLocation) { clearPreview(el); @@ -39,13 +39,13 @@ export default function (mind) { let meet: Element; const threshold = 12; - mind.map.addEventListener('dragstart', function (e) { + const onDragStart = function (e) { dragged = e.target; (dragged.parentNode.parentNode as Group).style.opacity = '0.5'; dragMoveHelper.clear(); - }); + }; - mind.map.addEventListener('dragend', async function (e: DragEvent) { + const onDragEnd = async function (e: DragEvent) { e.preventDefault(); (e.target as HTMLElement).style.opacity = ''; clearPreview(meet); @@ -65,38 +65,43 @@ export default function (mind) { } (dragged.parentNode.parentNode as Group).style.opacity = '1'; dragged = null; - }); + }; - mind.map.addEventListener( - 'dragover', - throttle(function (e: DragEvent) { - // console.log('drag', e) - clearPreview(meet); - // minus threshold infer that postion of the cursor is above topic - const topMeet = $d.elementFromPoint(e.clientX, e.clientY - threshold); - if (canPreview(topMeet, dragged)) { - meet = topMeet; - const y = topMeet.getBoundingClientRect().y; - if (e.clientY > y + topMeet.clientHeight) { - insertLocation = 'after'; - } else if (e.clientY > y + topMeet.clientHeight / 2) { + const onDragOver = throttle(function (e: DragEvent) { + clearPreview(meet); + const topMeet = $d.elementFromPoint(e.clientX, e.clientY - threshold); + if (canPreview(topMeet, dragged)) { + meet = topMeet; + const y = topMeet.getBoundingClientRect().y; + if (e.clientY > y + topMeet.clientHeight) { + insertLocation = 'after'; + } else if (e.clientY > y + topMeet.clientHeight / 2) { + insertLocation = 'in'; + } + } else { + const bottomMeet = $d.elementFromPoint(e.clientX, e.clientY + threshold); + if (canPreview(bottomMeet, dragged)) { + meet = bottomMeet; + const y = bottomMeet.getBoundingClientRect().y; + if (e.clientY < y) { + insertLocation = 'before'; + } else if (e.clientY < y + bottomMeet.clientHeight / 2) { insertLocation = 'in'; } } else { - const bottomMeet = $d.elementFromPoint(e.clientX, e.clientY + threshold); - if (canPreview(bottomMeet, dragged)) { - meet = bottomMeet; - const y = bottomMeet.getBoundingClientRect().y; - if (e.clientY < y) { - insertLocation = 'before'; - } else if (e.clientY < y + bottomMeet.clientHeight / 2) { - insertLocation = 'in'; - } - } else { - insertLocation = meet = null; - } + insertLocation = meet = null; } - if (meet) insertPreview(meet, insertLocation); - }, 200) - ); + } + if (meet) insertPreview(meet, insertLocation); + }, 200); + + mind.map.addEventListener('dragstart', onDragStart); + mind.map.addEventListener('dragend', onDragEnd); + mind.map.addEventListener('dragover', onDragOver); + + mind.bus.addListener('destroy', () => { + mind.map.removeEventListener('dragstart', onDragStart); + mind.map.removeEventListener('dragend', onDragEnd); + mind.map.removeEventListener('dragover', onDragOver); + }); } diff --git a/packages/client/src/tiptap/wrappers/mind/mind-elixir/plugin/nodeMenu.tsx b/packages/client/src/tiptap/wrappers/mind/mind-elixir/plugin/nodeMenu.tsx index 664bf664..eb816f67 100644 --- a/packages/client/src/tiptap/wrappers/mind/mind-elixir/plugin/nodeMenu.tsx +++ b/packages/client/src/tiptap/wrappers/mind/mind-elixir/plugin/nodeMenu.tsx @@ -1,11 +1,93 @@ import tippy from 'tippy.js'; import ReactDOM from 'react-dom'; -import { Button, Tooltip, Space } from '@douyinfe/semi-ui'; -import { IconBold, IconFont, IconMark } from '@douyinfe/semi-icons'; +import { useCallback, useEffect, useRef, useState } from 'react'; +import { Button, Tooltip, Space, Dropdown, Form } from '@douyinfe/semi-ui'; +import { IconBold, IconFont, IconMark, IconLink } from '@douyinfe/semi-icons'; import { ColorPicker } from 'tiptap/menus/_components/color-picker'; import { findEle } from '../utils/dom'; +import { FormApi } from '@douyinfe/semi-ui/lib/es/form'; + +export const Link = ({ mind, link, setLink }) => { + const $form = useRef(); + const $input = useRef(); + const [initialState, setInitialState] = useState({ link }); + + const handleOk = useCallback(() => { + $form.current.validate().then((values) => { + setLink(values.link); + }); + }, [setLink]); + + useEffect(() => { + setInitialState({ link }); + }, [link]); + + return ( + +
($form.current = formApi)} + labelPosition="left" + onSubmit={handleOk} + layout="horizontal" + > + (mind.isInnerEditing = true)} + onBlur={() => (mind.isInnerEditing = false)} + /> + + + + } + > + + +