tiptap: improve mind operations

This commit is contained in:
fantasticit 2022-05-01 13:06:16 +08:00
parent a7d3ff0005
commit c1a4e4b35e
18 changed files with 303 additions and 379 deletions

View File

@ -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)`,

View File

@ -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);

View File

@ -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]);

View File

@ -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,
});
});
};

View File

@ -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',
},
};

View File

@ -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;

View File

@ -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);

View File

@ -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);
});
}

View File

@ -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;

View File

@ -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()) {

View File

@ -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,
// }
// )
// }
}

View File

@ -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);
});
}

View File

@ -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<FormApi>();
const $input = useRef<HTMLInputElement>();
const [initialState, setInitialState] = useState({ link });
const handleOk = useCallback(() => {
$form.current.validate().then((values) => {
setLink(values.link);
});
}, [setLink]);
useEffect(() => {
setInitialState({ link });
}, [link]);
return (
<Dropdown
stopPropagation
zIndex={10000}
trigger="click"
position={'bottomLeft'}
render={
<div style={{ padding: 12 }}>
<Form
initValues={initialState}
getFormApi={(formApi) => ($form.current = formApi)}
labelPosition="left"
onSubmit={handleOk}
layout="horizontal"
>
<Form.Input
autofocus
label="链接"
field="link"
placeholder="请输入外链地址"
onFocus={() => (mind.isInnerEditing = true)}
onBlur={() => (mind.isInnerEditing = false)}
/>
<Button type="primary" htmlType="submit">
</Button>
</Form>
</div>
}
>
<span style={{ display: 'inline-block' }}>
<Tooltip content="设置链接" zIndex={10000}>
<Button type="tertiary" theme="borderless" size="small" icon={<IconLink style={{ fontSize: '0.85em' }} />} />
</Tooltip>
</span>
</Dropdown>
);
};
const Toolbar = ({ mind, toggleBold, setFontColor, setBackgroundColor, setLink }) => {
const [textColor, setTextColor] = useState('');
const [bgColor, setBgColor] = useState('');
const [hyperLink, setHyperLink] = useState('');
useEffect(() => {
const listener = function (nodeObj, clickEvent) {
if (!clickEvent) return;
if (nodeObj.style) {
setTextColor(nodeObj.style.color);
setBgColor(nodeObj.style.background);
} else {
setTextColor('');
setBgColor('');
}
setHyperLink(nodeObj.hyperLink);
};
mind.bus.addListener('selectNode', listener);
return () => {
mind.bus.removeListener('selectNode', listener);
};
}, []);
const Toolbar = ({ toggleBold, setFontColor, setBackgroundColor }) => {
return (
<Space spacing={4}>
<Tooltip content="加粗" zIndex={10000}>
@ -24,7 +106,29 @@ const Toolbar = ({ toggleBold, setFontColor, setBackgroundColor }) => {
}}
>
<Tooltip content="文本色" zIndex={10000}>
<Button type="tertiary" theme="borderless" size="small" icon={<IconFont style={{ fontSize: '0.85em' }} />} />
<Button
type="tertiary"
theme="borderless"
size="small"
icon={
<div
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}}
>
<IconFont style={{ fontSize: '0.85em' }} />
<span
style={{
width: 12,
height: 2,
backgroundColor: textColor,
}}
></span>
</div>
}
/>
</Tooltip>
</ColorPicker>
@ -37,6 +141,8 @@ const Toolbar = ({ toggleBold, setFontColor, setBackgroundColor }) => {
<Button type="tertiary" theme="borderless" size="small" icon={<IconMark />} />
</Tooltip>
</ColorPicker>
<Link mind={mind} link={hyperLink} setLink={setLink} />
</Space>
);
};
@ -81,8 +187,19 @@ export default function (mind) {
mind.updateNodeIcons(mind.currentNode.nodeObj, newIcons);
}
function setLink(link: string) {
if (!mind.currentNode) return;
mind.updateNodeHyperLink(mind.currentNode.nodeObj, link);
}
ReactDOM.render(
<Toolbar toggleBold={toggleBold} setFontColor={setFontColor} setBackgroundColor={setBackgroundColor} />,
<Toolbar
mind={mind}
toggleBold={toggleBold}
setFontColor={setFontColor}
setBackgroundColor={setBackgroundColor}
setLink={setLink}
/>,
menuContainer
);
@ -94,20 +211,27 @@ export default function (mind) {
trigger: 'manual',
placement: 'top',
hideOnClick: 'toggle',
offset: [-42, 40],
offset: [-45, 45],
appendTo: mind.container,
}) as any;
mind.bus.addListener('selectNode', function (nodeObj, clickEvent) {
if (!clickEvent) return;
const onSelectNode = function (nodeObj) {
const element = findEle(nodeObj.id, mind) as HTMLElement;
toolbarInstance.setProps({
getReferenceClientRect: () => element.getBoundingClientRect(),
});
toolbarInstance.show();
});
};
mind.bus.addListener('selectNode', onSelectNode);
mind.bus.addListener('selectNewNode', onSelectNode);
mind.bus.addListener('unselectNode', function () {
toolbarInstance.hide();
});
mind.bus.addListener('removeNode', function (nodeObj) {
const isRemoveCurrentNode = mind.currentNode && mind.currentNode.nodeObj.id === nodeObj.id;
if (isRemoveCurrentNode) {
toolbarInstance.hide();
}
});
}

View File

@ -9,46 +9,11 @@ import {
IconZoomOut,
IconZoomIn,
} from 'components/icons';
function Direction({ mind }) {
return (
<Space spacing={4}>
<Button
type="tertiary"
theme="borderless"
size="small"
onClick={() => {
mind.initLeft();
}}
icon={<IconMindLeft style={{ fontSize: '0.85em' }} />}
/>
<Button
type="tertiary"
theme="borderless"
size="small"
onClick={() => {
mind.initRight();
}}
icon={<IconMindRight style={{ fontSize: '0.85em' }} />}
/>
<Button
type="tertiary"
theme="borderless"
size="small"
onClick={() => {
mind.initSide();
}}
icon={<IconMindSide style={{ fontSize: '0.85em' }} />}
/>
</Space>
);
}
import { Divider } from 'tiptap/divider';
function Operation({ mind }) {
return (
<Space spacing={4}>
<Space spacing={2}>
<Button
type="tertiary"
theme="borderless"
@ -90,22 +55,45 @@ function Operation({ mind }) {
}}
icon={<IconZoomIn style={{ fontSize: '0.85em' }} />}
/>
<Divider />
<Button
type="tertiary"
theme="borderless"
size="small"
onClick={() => {
mind.initLeft();
}}
icon={<IconMindLeft style={{ fontSize: '0.85em' }} />}
/>
<Button
type="tertiary"
theme="borderless"
size="small"
onClick={() => {
mind.initRight();
}}
icon={<IconMindRight style={{ fontSize: '0.85em' }} />}
/>
<Button
type="tertiary"
theme="borderless"
size="small"
onClick={() => {
mind.initSide();
}}
icon={<IconMindSide style={{ fontSize: '0.85em' }} />}
/>
</Space>
);
}
export default function (mind) {
{
const div = document.createElement('div');
div.className = 'toolbar lt';
ReactDOM.render(<Direction mind={mind} />, div);
mind.container.append(div);
}
{
const div = document.createElement('div');
div.className = 'toolbar rb';
ReactDOM.render(<Operation mind={mind} />, div);
mind.container.append(div);
}
const div = document.createElement('div');
div.className = 'toolbar rb';
ReactDOM.render(<Operation mind={mind} />, div);
mind.container.append(div);
}

View File

@ -1,8 +1,8 @@
import { LEFT, RIGHT, SIDE } from '../const';
import { NodeObj } from '../index';
import { encodeHTML } from '../utils/index';
export type Top = HTMLElement;
export type Top = HTMLElement;
export type Group = HTMLElement;
export interface Topic extends HTMLElement {

View File

@ -1,103 +0,0 @@
// WIP
export function pieChart(data) {
const cx = 45 // circle center
const cy = 45
const r = 35 // radius
const lx = 105 // description position
const ly = 15
const svgns = 'http://www.w3.org/2000/svg'
const colors = [
'#004c6d',
'#346888',
'#5886a5',
'#7aa6c2',
'#9dc6e0',
'#c1e7ff',
]
// create <svg /> with specific width and height
const chart = document.createElementNS(svgns, 'svg:svg')
chart.setAttribute('width', 160)
chart.setAttribute('height', 90)
// chart.setAttribute("viewBox", "0 0 " + width + " " + height);
chart.setAttribute('style', 'display:block;')
let total = 0
for (let i = 0; i < data.length; i++) {
total += data[i].value
}
const angles = []
for (let i = 0; i < data.length; i++) {
angles[i] = (data[i].value / total) * Math.PI * 2
}
let starttangle = 0
for (let i = 0; i < data.length; i++) {
const endangle = starttangle + angles[i]
const x1 = cx + r * Math.sin(starttangle)
const y1 = cy - r * Math.cos(starttangle)
const x2 = cx + r * Math.sin(endangle)
const y2 = cy - r * Math.cos(endangle)
const path = document.createElementNS(svgns, 'path')
const d = `M ${cx},${cy} L ${x1},${y1} A ${r},${r} 0 0 1 ${x2},${y2} Z`
path.setAttribute('d', d)
path.setAttribute('fill', colors[i])
chart.appendChild(path)
starttangle = endangle
// description
const icon = document.createElementNS(svgns, 'rect')
icon.setAttribute('x', lx)
icon.setAttribute('y', ly + 15 * i)
icon.setAttribute('width', 10)
icon.setAttribute('height', 10)
icon.setAttribute('fill', colors[i])
chart.appendChild(icon)
const label = document.createElementNS(svgns, 'text')
label.setAttribute('x', lx + 50)
label.setAttribute('y', ly + 15 * i + 10)
label.setAttribute('font-family', 'sans-serif')
label.setAttribute('font-size', '14')
label.appendChild(document.createTextNode(data[i].label))
chart.appendChild(label)
}
return chart
}
// svg style
// background-color: #fff;
// padding: 5px;
// border-radius: 5px;
// border: 1px solid #ccc;
// example
if (nodeObj.pie) {
const pieContainer = $d.createElement('div')
pieContainer.className = 'pie'
const pieData = [
{
value: 12,
label: 'a',
},
{
value: 15,
label: 'b',
},
{
value: 42,
label: 'c',
},
{
value: 33,
label: 'd',
},
]
pieContainer.appendChild(pieChart(pieData))
tpc.appendChild(pieContainer)
}

View File

@ -30,4 +30,8 @@ Bus.prototype = {
}
}
},
destroy: function () {
this.fire('destroy');
this.handlers = {};
},
};

View File

@ -5,7 +5,7 @@ export const createMainPath = function (d: string) {
path.setAttribute('d', d);
path.setAttribute('stroke', '#4f83fd');
path.setAttribute('fill', 'none');
path.setAttribute('stroke-width', '2');
path.setAttribute('stroke-width', '3');
return path;
};
@ -21,7 +21,7 @@ export const createLine = function (x1: number, y1: number, x2: number, y2: numb
line.setAttribute('y1', y1);
line.setAttribute('x2', x2);
line.setAttribute('y2', y2);
line.setAttribute('stroke', '#f00');
line.setAttribute('stroke', '#6ec4c4');
line.setAttribute('fill', 'none');
line.setAttribute('stroke-width', '2');
return line;
@ -33,7 +33,7 @@ export const createPath = function (d: string) {
path.setAttribute('stroke', '#6e80db');
path.setAttribute('fill', 'none');
path.setAttribute('stroke-linecap', 'square');
path.setAttribute('stroke-width', '1');
path.setAttribute('stroke-width', '2');
path.setAttribute('transform', 'translate(0.5,-0.5)');
// adding translate(0.5,-0.5) can fix render error on windows, but i still dunno why
return path;
@ -47,12 +47,12 @@ export const createSvgGroup = function (d: string, arrowd: string): CustomSvg {
const path = $d.createElementNS(svgNS, 'path');
const arrow = $d.createElementNS(svgNS, 'path');
arrow.setAttribute('d', arrowd);
arrow.setAttribute('stroke', 'rgb(235, 95, 82)');
arrow.setAttribute('stroke', '#6ec4c4');
arrow.setAttribute('fill', 'none');
arrow.setAttribute('stroke-linecap', 'cap');
arrow.setAttribute('stroke-width', '2');
path.setAttribute('d', d);
path.setAttribute('stroke', 'rgb(235, 95, 82)');
path.setAttribute('stroke', '#6ec4c4');
path.setAttribute('fill', 'none');
path.setAttribute('stroke-linecap', 'cap');
path.setAttribute('stroke-width', '2');