Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

treechartjs

Package Overview
Dependencies
Maintainers
1
Versions
13
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

treechartjs - npm Package Compare versions

Comparing version 0.0.1 to 0.0.2

1458

dist/index.js

@@ -1,1457 +0,1 @@

(function(l, r) { if (l.getElementById('livereloadscript')) return; r = l.createElement('script'); r.async = 1; r.src = '//' + (window.location.host || 'localhost').split(':')[0] + ':35729/livereload.js?snipver=1'; r.id = 'livereloadscript'; l.getElementsByTagName('head')[0].appendChild(r) })(window.document);
(function () {
'use strict';
class FollowScroll {
constructor(option) {
const { scrollContainer, eventContainer, autoScrollTriggerDistance, autoScrollSpeed } = option;
this.scrollContainer = scrollContainer;
this.eventContainer = eventContainer;
this.autoScrollSpeed = autoScrollSpeed;
this.triggerDistance = autoScrollTriggerDistance || 0;
this.targetNode = null;
this.interval = 0;
this.directData = {
left: false,
right: false,
top: false,
bottom: false
};
this.setEvent();
}
setEvent() {
const { eventContainer } = this;
this.mouseMoveHandler = e => {
const { button, movementX, movementY } = e;
// 处理Chrome76版本长按不移动也会触发的情况
if (movementX === 0 && movementY === 0) return
if (button || !this.targetNode) return
this.setDirectData(e);
this.triggerScroll();
};
eventContainer.addEventListener('mousemove', this.mouseMoveHandler);
}
setDirectData(event) {
const { movementX, movementY } = event;
const { scrollContainer, targetNode, triggerDistance, directData } = this;
const { scrollLeft, scrollTop, scrollWidth, scrollHeight, clientWidth, clientHeight } = scrollContainer;
const { left: scrollContainerLeft, right: scrollContainerRight, top: scrollContainerTop, bottom: scrollContainerBottom } = scrollContainer.getBoundingClientRect();
const { left: targetNodeLeft, right: targetNodeRight, top: targetNodeTop, bottom: targetNodeBottom } = targetNode.getBoundingClientRect();
if (targetNodeLeft - scrollContainerLeft < triggerDistance) directData.left = true;
if (targetNodeTop - scrollContainerTop < triggerDistance) directData.top = true;
if (scrollContainerRight - targetNodeRight < triggerDistance) directData.right = true;
if (scrollContainerBottom - targetNodeBottom < triggerDistance) directData.bottom = true;
if (movementX > 0 || scrollLeft === 0) directData.left = false;
if (movementY > 0 || scrollTop === 0) directData.top = false;
if (movementX < 0 || scrollLeft + clientWidth >= scrollWidth) directData.right = false;
if (movementY < 0 || scrollTop + clientHeight >= scrollHeight) directData.bottom = false;
}
triggerScroll() {
const { directData, scrollContainer, autoScrollSpeed } = this;
let existDirect = false;
for (const key in directData) {
if (directData[key]) {
existDirect = true;
break
}
}
if (!existDirect) return this.stop(true)
if (this.interval) return
this.interval = setInterval(() => {
let { scrollLeft, scrollTop } = scrollContainer;
if (directData.left) scrollLeft -= autoScrollSpeed;
if (directData.right) scrollLeft += autoScrollSpeed;
if (directData.top) scrollTop -= autoScrollSpeed;
if (directData.bottom) scrollTop += autoScrollSpeed;
scrollContainer.scrollLeft = scrollLeft;
scrollContainer.scrollTop = scrollTop;
}, 20);
}
start(targetNode) {
this.targetNode = targetNode;
}
stop(keepTargetNode = false) {
if (!keepTargetNode) this.targetNode = null;
if (this.interval) {
clearInterval(this.interval);
this.interval = 0;
}
}
destroy() {
this.eventContainer.removeEventListener('mousemove', this.mouseMoveHandler);
this.targetNode = this.scrollContainer = this.eventContainer = null;
this.interval = 0;
}
}
const isElement = data => /HTML/.test(Object.prototype.toString.call(data)) && data.nodeType === 1;
const isNumber = data => /Number/.test(Object.prototype.toString.call(data));
const setNotAllowEffect = node => node.classList.add('show-not-allow');
// 求数组的交集
const getArrayIntersection = (...arrays) => {
const arrayCount = arrays.length;
if (arrayCount < 2) return []
const result = [];
const countMap = {};
arrays.reduce((a, b) => a.concat(b), []).forEach(item => {
if (countMap[item]) {
countMap[item]++;
countMap[item] === arrayCount && result.push(item);
} else {
countMap[item] = 1;
}
});
return result
};
class TreeChart {
/* ======== API ======== */
getKeyByElement(nodeElement) {
if (!isElement(nodeElement)) return null
return nodeElement.classList.contains('tree-chart-node') ? nodeElement.getAttribute('data-key') : null
}
getNodeElement(key) {
return this.nodesContainer.querySelector(`.tree-chart-item-${key}`)
}
getPreviousKey(key) {
try {
const nodeElement = this.getNodeElement(key);
return this.getKeyByElement(nodeElement.parentElement.previousElementSibling.querySelector('.tree-chart-node'))
} catch (e) {
return null
}
}
getNextKey(key) {
try {
const nodeElement = this.getNodeElement(key);
return this.getKeyByElement(nodeElement.parentElement.nextElementSibling.querySelector('.tree-chart-node'))
} catch (e) {
return null
}
}
getParentKey(key) {
try {
const nodeElement = this.getNodeElement(key);
return this.getKeyByElement(nodeElement.parentElement.parentElement.previousElementSibling)
} catch (e) {
return null
}
}
getChildrenKeys(key) {
const childrenKeys = this.getNodeElement(key).getAttribute('data-children');
if (!childrenKeys) return []
return childrenKeys.split(',')
}
existChildren(key) {
const nodeElement = this.getNodeElement(key);
if (!nodeElement) return false
return Boolean(nodeElement.getAttribute('data-children'))
}
insertNode(targetKey, origin, type) {
const targetNode = this.getNodeElement(targetKey);
// 限制不能给根节点添加兄弟元素
if (/next|previous/.test(type) && targetNode === this.rootNode) return
// 处理origin部分
// 是否需要新建节点
const needCreateNode = /Object/.test(Object.prototype.toString.call(origin));
let originKey = null;
let originNode = null;
let originNodeContainer = null;
if (needCreateNode) {
originKey = this.getKeyField(origin);
originNode = this.createNode(origin);
originNodeContainer = this.createNodeContainer();
originNodeContainer.appendChild(originNode);
this.setNodeEvent(originNode);
} else {
originKey = origin;
originNode = this.getNodeElement(originKey);
originNodeContainer = originNode.parentElement;
// 修改原先节点父节点的data-children属性
const originParentKey = this.getParentKey(originKey);
this.removeChildrenKey(originParentKey, originKey);
// 如果移动节点后原位置没有兄弟节点的话移除childrenContainer容器和展开收起按钮
if (!this.existChildren(originParentKey)) {
this.removeChildrenContainer(originParentKey);
this.allowFold && this.removeFoldButton(originParentKey);
}
}
// 处理target部分
if (type === 'child') {
// 本身存在子节点的情况,直接插入
if (this.existChildren(targetKey)) {
this.getChildrenContainer(targetKey).appendChild(originNodeContainer);
// 如果目标节点是折叠状态,插入子节点后自动展开
this.nodeIsFold(targetKey) && this.toggleFold(targetKey);
} else {
// 没有任何子节点的话创建一个容器
const newChildrenContainer = this.createChildrenContainer();
newChildrenContainer.appendChild(originNodeContainer);
targetNode.parentElement.appendChild(newChildrenContainer);
// 没有展开按钮需要新增
this.createFoldButton(targetNode);
}
this.addChildrenKey(targetKey, originKey);
} else {
const targetParentKey = this.getParentKey(targetKey);
const parentChildrenContainer = this.getChildrenContainer(targetParentKey);
const targetNodeContainer = targetNode.parentElement;
if (type === 'previous') parentChildrenContainer.insertBefore(originNodeContainer, targetNodeContainer);
if (type === 'next') parentChildrenContainer.insertBefore(originNodeContainer, targetNodeContainer.nextElementSibling);
this.addChildrenKey(targetParentKey, originKey);
}
this.reloadLink();
}
removeNode(targetKey) {
const targetNode = this.getNodeElement(targetKey);
if (!targetNode) return
// 不支持移除root节点
if (targetNode === this.rootNode) return console.warn('not allow remove root node')
// 修改父节点的data-children
const parentNodeKey = this.getParentKey(targetKey);
this.removeChildrenKey(parentNodeKey, targetKey);
// 移除当前节点
const targetNodeContainer = targetNode.parentElement;
targetNodeContainer.parentElement.removeChild(targetNodeContainer);
// 如果当前节点是唯一的子节点就移除父节点的子容器
if (!this.existChildren(parentNodeKey)) {
this.removeChildrenContainer(parentNodeKey);
this.allowFold && this.removeFoldButton(parentNodeKey);
}
this.reloadLink();
}
nodeIsFold(targetKey) {
return this.getFoldButton(targetKey).classList.contains('is-fold')
}
toggleFold(targetKey) {
const foldButton = this.getFoldButton(targetKey);
if (!foldButton) return
const childNodeContainer = this.getChildrenContainer(targetKey);
if (this.nodeIsFold(targetKey)) {
childNodeContainer.classList.remove('is-hidden');
foldButton.classList.remove('is-fold');
} else {
childNodeContainer.classList.add('is-hidden');
foldButton.classList.add('is-fold');
}
this.reloadLink();
}
reRenderNode(targetKey, data) {
const oldNodeKey = targetKey.toString();
const newNodeKey = this.getKeyField(data);
const node = this.getNodeElement(oldNodeKey);
const childrenKeys = this.getChildrenKeys(targetKey);
// 更新key
if (newNodeKey !== oldNodeKey) {
const { linkContainer } = this;
const parentNodeKey = this.getParentKey(oldNodeKey);
// 替换父节点的children-key
this.replaceChildrenKey(parentNodeKey, oldNodeKey, newNodeKey);
// 替换line的类名
const oldLineClassName = `line-${parentNodeKey}-${oldNodeKey}`;
const parentLine = linkContainer.querySelector(`.${oldLineClassName}`);
parentLine.classList.remove(oldLineClassName);
parentLine.classList.add(`line-${parentNodeKey}-${newNodeKey}`);
childrenKeys.forEach(childKey => {
const oldChildLineClassName = `line-${oldNodeKey}-${childKey}`;
const childLink = this.linkContainer.querySelector(`.${oldChildLineClassName}`);
childLink.classList.remove(oldChildLineClassName);
childLink.classList.add(`line-${newNodeKey}-${childKey}`);
});
// 更新position数据
this.replacePositionNodeKey(oldNodeKey, newNodeKey);
}
// 替换节点
const newNode = this.createNode(data);
childrenKeys.length && newNode.setAttribute('data-children', childrenKeys.join());
node.querySelector('.tree-chart-unfold') && this.createFoldButton(newNode);
const nodeContainer = node.parentElement;
nodeContainer.insertBefore(newNode, node);
nodeContainer.removeChild(node);
}
reloadLink() {
this.createLink();
this.resize();
}
reRender(data) {
const { nodesContainer } = this;
// 更新nodes
nodesContainer.innerHTML = '';
const nodeContainer = this.createNodeContainer();
nodesContainer.appendChild(nodeContainer);
this.createNodes(data, nodeContainer, true);
// 更新links
this.reloadLink();
}
/* ================ */
// 需要取消注册的才使用该函数
registerEvent(eventType, handler, eventTarget = window) {
if (!this.eventList) this.eventList = [];
const handlerFunction = handler.bind(this);
this.eventList.push({ eventTarget, type: eventType, handler: handlerFunction });
eventTarget.addEventListener(eventType, handlerFunction);
}
unregisterEvent() {
this.eventList.forEach(item => item.eventTarget.removeEventListener(item.type, item.handler));
}
addChildrenKey(targetKey, newKey) {
const targetNodeElement = this.getNodeElement(targetKey);
const childrenKeys = targetNodeElement.getAttribute('data-children') || '';
targetNodeElement.setAttribute('data-children', childrenKeys ? `${childrenKeys},${newKey}` : newKey);
}
removeChildrenKey(targetKey, removeKey) {
const childrenKeys = this.getChildrenKeys(targetKey);
if (!childrenKeys.length) return
const index = childrenKeys.indexOf(removeKey);
index > -1 && childrenKeys.splice(index, 1);
const targetNodeElement = this.getNodeElement(targetKey);
if (childrenKeys.length) {
targetNodeElement.setAttribute('data-children', childrenKeys.join());
} else {
targetNodeElement.removeAttribute('data-children');
}
}
replaceChildrenKey(targetKey, oldKey, newKey) {
const childrenKeys = this.getChildrenKeys(targetKey);
if (!childrenKeys.length) return
const index = childrenKeys.indexOf(oldKey);
if (index === -1) return
childrenKeys.splice(index, 1, newKey);
this.getNodeElement(targetKey).setAttribute('data-children', childrenKeys.join());
}
createChildrenContainer(className) {
const container = document.createElement('div');
container.classList.add('tree-chart-children-container');
className && container.classList.add(className);
return container
}
getChildrenContainer(targetKey) {
return this.getNodeElement(targetKey).nextElementSibling
}
removeChildrenContainer(targetKey) {
const container = this.getChildrenContainer(targetKey);
container.parentElement.removeChild(container);
}
getKeyField(data) {
return data[this.option.keyField].toString() || null
}
constructor(option) {
this.mergeOption(option);
this.createChartElement();
this.resize();
this.setEvent();
}
mergeOption(data) {
const option = {
keyField: 'id', // 作为唯一ID的字段
isVertical: true,
distanceX: 40, // item的垂直间距,不小于40
distanceY: 40, // item的水平间距,不小于40
allowFold: false, // 是否能折叠
foldNodeKeys: [], // 初始需要折叠的节点
draggable: false, // 是否能拖拽item
dragScroll: false, // 是否开启拖拽滚动
autoScrollTriggerDistance: 50, // 自动触发滚动的距离
autoScrollSpeed: 6, // 滚动速度
line: {
type: 'bezier',
smooth: 50 // 光滑程度(0-100,100为直线)
},
...data
};
const {
draggable,
allowFold,
dragScroll,
isVertical,
line,
padding,
distanceX,
distanceY
} = option;
if (distanceX < 40) option.distanceX = 40;
if (distanceY < 40) option.distanceY = 40;
this.draggable = draggable;
this.allowFold = allowFold;
this.dragScroll = dragScroll;
this.isVertical = isVertical;
this.lineConfig = line;
// draggable时候最小内边距不能小于30
const containerPadding = {
top: 30,
right: 30,
bottom: 30,
left: 30,
...padding
};
for (const key in containerPadding) {
let paddingValue = containerPadding[key];
if (!isNumber(paddingValue)) paddingValue = 0;
if (draggable && paddingValue < 30) paddingValue = 30;
containerPadding[key] = paddingValue;
}
this.containerPadding = containerPadding;
this.option = option;
this.initHooks();
}
initHooks() {
const controlHooks = [
'nodeControl',
'preventDrag'
];
const eventHooks = [
'dragStart',
'dragEnd',
'click',
'mouseEnter',
'mouseLeave'
];
const hookList = ['contentRender', ...controlHooks, ...eventHooks];
this.hooks = {};
hookList.forEach(name => {
const handler = this.option[name];
if (typeof handler === 'function') this.hooks[name] = handler;
});
}
createChartElement() {
const { isVertical } = this;
const { container, data } = this.option;
container.classList.add('tree-chart');
isVertical && container.classList.add('is-vertical');
this.container = container;
this.createNodes(data, this.createNodesContainer());
this.createLinkContainer();
this.createLink();
this.createGhostContainer();
}
createNodesContainer() {
const { container, containerPadding } = this;
const nodesContainer = this.createNodeContainer();
nodesContainer.classList.add('is-nodes-container');
for (const direct in containerPadding) {
if (!/left|top|right|bottom/.test(direct)) continue
const paddingValue = containerPadding[direct];
nodesContainer.style[`padding${direct.replace(/^./, $ => $.toUpperCase())}`] = `${paddingValue}px`;
}
container.appendChild(nodesContainer);
this.nodesContainer = nodesContainer;
const nodeContainer = this.createNodeContainer();
nodesContainer.append(nodeContainer);
return nodeContainer
}
createNodeContainer() {
const nodeContainer = document.createElement('div');
nodeContainer.classList.add('tree-chart-container');
return nodeContainer
}
// 数据数据结构生成节点
createNodes(data, parentNodeContainer, reRender) {
const { allowFold, rootNode, option } = this;
const { children } = data;
const { foldNodeKeys } = option;
const existChildren = Array.isArray(children) && children.length > 0;
// 创建节点
const node = this.createNode(data);
parentNodeContainer.appendChild(node);
// 初始化或重新渲染的时候
if (!rootNode || reRender) this.rootNode = node;
if (existChildren) {
const childrenContainer = this.createChildrenContainer();
parentNodeContainer.appendChild(childrenContainer);
if (allowFold) {
const foldButton = this.createFoldButton(node);
// 节点需要初始折叠
if (foldNodeKeys.includes(this.getKeyField(data))) {
foldButton.classList.add('is-fold');
childrenContainer.classList.add('is-hidden');
}
}
const childKeys = [];
children.forEach(childData => {
childKeys.push(this.getKeyField(childData));
const childNodeContainer = this.createNodeContainer();
childrenContainer.appendChild(childNodeContainer);
this.createNodes(childData, childNodeContainer);
});
node.setAttribute('data-children', childKeys.join());
}
}
createNode(data) {
const { draggable, hooks, option } = this;
const { distanceX, distanceY } = option;
const { contentRender, nodeControl } = hooks;
const node = document.createElement('div');
const key = this.getKeyField(data);
node.classList.add('tree-chart-node', `tree-chart-item-${key}`);
node.setAttribute('data-key', key);
node.style.marginBottom = `${distanceY}px`;
node.style.marginRight = `${distanceX}px`;
const renderContainer = document.createElement('div');
renderContainer.classList.add('tree-render-container');
// 生成用户自定义模板
if (contentRender) {
const renderResult = contentRender(data);
if (typeof renderResult === 'string') {
renderContainer.innerHTML = renderResult.replace(/>\s+</g, '><');
} else if (isElement(renderResult)) {
renderContainer.appendChild(renderResult);
} else {
renderContainer.innerText = 'Please check contentRender return type is string or element';
}
} else {
renderContainer.innerText = 'option.contentRender is required';
}
node.appendChild(renderContainer);
// 添加节点事件
this.setNodeEvent(node);
// 拖拽控制
if (draggable && nodeControl) {
const controlConfig = {
draggable: true,
insertChild: true,
insertPrevious: true,
insertNext: true,
...nodeControl(data)
};
!controlConfig.draggable && node.classList.add('not-allow-drag');
!controlConfig.insertChild && node.classList.add('not-allow-insert-child');
!controlConfig.insertPrevious && node.classList.add('not-allow-insert-previous');
!controlConfig.insertNext && node.classList.add('not-allow-insert-next');
}
return node
}
// 设置拖动镜像容器
createGhostContainer() {
const { container } = this;
const ghostContainer = document.createElement('div');
ghostContainer.classList.add('tree-chart-ghost-container');
container.appendChild(ghostContainer);
this.ghostContainer = ghostContainer;
}
// 创建展开收起按钮
createFoldButton(nodeElement) {
if (nodeElement.querySelector('.tree-chart-unfold')) return
const button = document.createElement('div');
button.classList.add('tree-chart-unfold');
button.innerHTML = '<div></div><div></div>';
nodeElement.appendChild(button);
return button
}
getFoldButton(targetKey) {
const targetNodeElement = this.getNodeElement(targetKey);
return targetNodeElement.querySelector('.tree-chart-unfold')
}
removeFoldButton(targetKey) {
const foldButton = this.getFoldButton(targetKey);
foldButton && foldButton.parentElement.removeChild(foldButton);
}
createLinkContainer() {
const { container } = this;
const linkContainer = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
linkContainer.classList.add('tree-chart-link-container');
container.appendChild(linkContainer);
this.linkContainer = linkContainer;
}
// 根据节点间父子关系生成连线信息,同时生成节点位置数据
createLink() {
const { container, nodesContainer, linkContainer, draggable, isVertical } = this;
const { left: containerLeft, top: containerTop } = container.getBoundingClientRect();
const { scrollLeft, scrollTop } = container;
linkContainer.innerHTML = '';
draggable && this.initPositionData();
nodesContainer.querySelectorAll('.tree-chart-node').forEach(nodeElement => {
const childrenKeys = nodeElement.getAttribute('data-children');
const { left: nodeLeft, right: nodeRight, top: nodeTop, bottom: nodeBottom } = nodeElement.getBoundingClientRect();
const nodeKey = this.getKeyByElement(nodeElement);
const childrenNodeContainer = nodeElement.nextElementSibling;
// 忽略收起状态的节点
if (childrenKeys && !childrenNodeContainer.classList.contains('is-hidden')) {
const from = {
x: nodeLeft - containerLeft + scrollLeft + (isVertical ? nodeElement.offsetWidth / 2 : nodeElement.offsetWidth),
y: nodeTop - containerTop + scrollTop + (isVertical ? nodeElement.offsetHeight : nodeElement.offsetHeight / 2),
key: nodeKey
};
childrenKeys.split(',').forEach(childNodeKey => {
const childElement = this.getNodeElement(childNodeKey);
const { left: childElementLeft, top: childElementTop } = childElement.getBoundingClientRect();
const { offsetWidth: childElementWidth, offsetHeight: childElementHeight } = childElement;
const to = {
x: childElementLeft - containerLeft + scrollLeft + (isVertical ? childElementWidth / 2 : 0),
y: childElementTop - containerTop + scrollTop + (isVertical ? 0 : childElement.offsetHeight / 2),
key: childNodeKey
};
this.drawLine(from, to);
});
}
draggable && this.addPositionData(nodeKey, {
left: nodeLeft - containerLeft + scrollLeft,
right: nodeRight - containerLeft + scrollLeft,
top: nodeTop - containerTop + scrollTop,
bottom: nodeBottom - containerTop + scrollTop
});
});
}
// 生成节点位置信息,方便后面的碰撞检测
initPositionData() {
this.positionData = {
left: { sortList: [] },
right: { sortList: [] },
top: { sortList: [] },
bottom: { sortList: [] },
node: {}
};
}
addPositionData(nodeKey, positionDataItem) {
const { positionData } = this;
// 保存节点位置信息
// nodeKey->{left,top,right,bottom }
positionData.node[nodeKey] = positionDataItem;
for (const direct in positionDataItem) {
// 获取节点方位数据值
const positionValue = positionDataItem[direct];
// 获取数据归类容器,形成position->[...nodeKey]的映射
const directPositionMap = positionData[direct];
if (directPositionMap[positionValue]) {
directPositionMap[positionValue].push(nodeKey);
} else {
directPositionMap[positionValue] = [nodeKey];
}
// 插入排序,并去重
// sortList->[...position]
const sortList = directPositionMap.sortList;
if (sortList.length) {
for (let index = 0, len = sortList.length; index < len; index++) {
const currentValue = sortList[index];
if (positionValue === currentValue) break
if (positionValue < currentValue) {
sortList.splice(index, 0, positionValue);
break
}
index === len - 1 && sortList.push(positionValue);
}
} else {
sortList.push(positionValue);
}
}
}
replacePositionNodeKey(oldKey, newKey) {
const { positionData } = this;
// 更新node集合
positionData.node[newKey] = positionData.node[oldKey];
delete positionData.node[oldKey];
void ['left', 'top', 'right', 'bottom'].forEach(direct => {
const directPositionMap = positionData[direct];
for (const positionValue in directPositionMap) {
if (positionValue === 'sortList') continue
const nodeKeyList = directPositionMap[positionValue];
const oldKeyIndex = nodeKeyList.indexOf(oldKey);
if (oldKeyIndex > -1) {
nodeKeyList.splice(oldKeyIndex, 1, newKey);
break
}
}
});
}
setEvent() {
const { allowFold, draggable, dragScroll } = this;
allowFold && this.setFoldEvent();
this.setClickHook();
draggable && this.setDragEvent();
dragScroll && this.setDragScroll();
}
setFoldEvent() {
this.nodesContainer.addEventListener('click', ({ target }) => {
if (!target.classList.contains('tree-chart-unfold')) return
this.toggleFold(this.getKeyByElement(target.parentElement));
});
}
// 两点间连线
drawLine(from, to, isTemp) {
const { linkContainer, isVertical, allowFold, lineConfig } = this;
const { type: lineType, smooth } = lineConfig;
const lineClassName = `line-${from.key}-${to.key}`;
let link = document.querySelector(`.${lineClassName}`);
if (!link) {
link = document.createElementNS('http://www.w3.org/2000/svg', 'path');
link.classList.add(lineClassName, `line-from-${from.key}`);
isTemp && link.classList.add('is-temp-line');
linkContainer.appendChild(link);
}
let path = '';
let { x: fromX, y: fromY } = from;
const { x: toX, y: toY } = to;
// 需要加上fold按钮的宽度
if (allowFold && this.existChildren(from.key)) isVertical ? fromY += 5 : fromX += 5;
const centerX = (toX - fromX) / 2;
const centerY = (toY - fromY) / 2;
const M = `${fromX} ${fromY}`;
const T = `${toX} ${toY}`;
if (lineType === 'straight') path = `M${M} T ${T}`;
if (lineType === 'broken') {
let L1 = '';
let L2 = '';
if (isVertical) {
L1 = `${fromX} ${fromY + centerY}`;
L2 = `${toX} ${toY - centerY}`;
} else {
L1 = `${fromX + centerX} ${fromY}`;
L2 = `${toX - centerX} ${toY}`;
}
path = `M${M} L${L1} L${L2} L${T}`;
}
if (lineType === 'bezier') {
const smoothScale = smooth / 100;
let Q1 = '';
if (isVertical) {
Q1 = `${fromX} ${fromY + (centerY - centerY * smoothScale)}`;
} else {
Q1 = `${fromX + (centerX - centerX * smoothScale)} ${fromY}`;
}
const Q2 = `${fromX + centerX} ${fromY + centerY}`;
path = `M${M} Q${Q1} ${Q2} T ${T}`;
}
link.setAttribute('d', path);
}
// 沿着事件触发路径向上找node节点
getCurrentEventNode(eventTarget) {
const { nodesContainer } = this;
// 忽略展开按钮
if (eventTarget.classList.contains('tree-chart-unfold')) return null
let searchElement = eventTarget;
while (nodesContainer !== searchElement) {
if (searchElement.classList.contains('tree-chart-node')) return searchElement
searchElement = searchElement.parentElement;
}
return null
}
// 判断是否处于拖动过程
isDragging() {
const { draggable, dragData } = this;
if (!draggable) return false
const { ghostTranslateX, ghostTranslateY, key } = dragData;
return key && (ghostTranslateX !== 0 || ghostTranslateY !== 0)
}
setClickHook() {
const { nodesContainer, hooks } = this;
const { click: clickHook } = hooks;
if (!clickHook) return
// 用mouseEvent来实现click主要是为了区别dragStart和click的行为
let mouseDownNode = null;
nodesContainer.addEventListener('mousedown', ({ button, target }) => {
if (button !== 0) return
mouseDownNode = this.getCurrentEventNode(target);
});
nodesContainer.addEventListener('mouseup', e => {
const { button, target } = e;
if (button !== 0) return
const mouseUpNode = this.getCurrentEventNode(target);
if (mouseUpNode && mouseUpNode === mouseDownNode && !this.isDragging()) {
clickHook({ key: this.getKeyByElement(mouseUpNode), element: mouseUpNode }, e);
}
});
}
setNodeEvent(node) {
const { mouseEnter: enterHook, mouseLeave: leaveHook } = this.hooks;
const argumentData = { key: this.getKeyByElement(node), element: node };
const contentElement = node.querySelector('.tree-render-container');
enterHook && contentElement.addEventListener('mouseenter', e => {
// 忽略拖动被覆盖的情况
if (this.isDragging()) return
enterHook(argumentData, e);
});
leaveHook && contentElement.addEventListener('mouseleave', e => {
// 忽略拖动被覆盖的情况
if (this.isDragging()) return
leaveHook(argumentData, e);
});
}
createNodeParams(nodeKey) {
return {
key: nodeKey,
parentKey: this.getParentKey(nodeKey),
previousKey: this.getPreviousKey(nodeKey),
nextKey: this.getNextKey(nodeKey)
}
}
initDragData() {
this.dragData = {
key: null,
ghostElement: null,
ghostTranslateX: 0,
ghostTranslateY: 0,
// mousedown事件在节点的触发的偏移位置
mouseDownOffsetX: 0,
mouseDownOffsetY: 0
};
}
// 绑定拖动事件
setDragEvent() {
const { ghostContainer, container, nodesContainer, hooks, option } = this;
const { autoScrollTriggerDistance, autoScrollSpeed } = option;
const { preventDrag, dragStart, dragEnd } = hooks;
// 是否触发dragStart事件
let emitDragStart = true;
this.initDragData();
nodesContainer.addEventListener('mousedown', e => {
const { button, clientX, clientY } = e;
if (button !== 0) return
const dragNode = this.getCurrentEventNode(e.target);
const dragNodeKey = this.getKeyByElement(dragNode);
// 根节点不允许拖动
if (!dragNode || dragNode === this.rootNode) return
// 用户禁止拖动的节点
if (dragNode.classList.contains('not-allow-drag')) return
// preventDrag返回true时阻止拖动
if (preventDrag && preventDrag({ key: dragNodeKey, element: dragNode }, e)) return
// 保存偏移量等
const { left: nodePositionLeft, top: nodePositionTop } = this.positionData.node[dragNodeKey];
const ghostElement = dragNode.cloneNode(true);
ghostElement.style.margin = '0px';
Object.assign(this.dragData, {
key: dragNodeKey,
ghostElement,
mouseDownOffsetX: clientX + container.scrollLeft - nodePositionLeft,
mouseDownOffsetY: clientY + container.scrollTop - nodePositionTop
});
// 跟随滚动
this.FollowScroll.start(ghostElement);
});
nodesContainer.addEventListener('mousemove', e => {
const { button, movementX, movementY, clientX, clientY } = e;
const { ghostElement, mouseDownOffsetX, mouseDownOffsetY, key } = this.dragData;
if (button !== 0 || !key) return
// 处理Chrome76版本长按不移动也会触发的情况
if (movementX === 0 && movementY === 0) return
// 清除文字选择对拖动的影响
getSelection && getSelection().removeAllRanges();
// 光标形状变为move
nodesContainer.classList.add('cursor-move');
// 添加镜像元素和同步位置
!ghostContainer.contains(ghostElement) && ghostContainer.appendChild(ghostElement);
const ghostTranslateX = clientX + container.scrollLeft - mouseDownOffsetX;
const ghostTranslateY = clientY + container.scrollTop - mouseDownOffsetY;
ghostElement.style.transform = `translate(${ghostTranslateX}px, ${ghostTranslateY}px)`;
Object.assign(this.dragData, {
ghostTranslateX,
ghostTranslateY
});
const ghostPosition = this.getGhostPosition();
this.setDragEffect(ghostPosition);
// 跟随滚动
if (dragStart && emitDragStart) {
emitDragStart = false;
dragStart({ key, element: this.getNodeElement(key) });
}
});
this.registerEvent('mouseup', () => {
emitDragStart = true;
const { dragData, nodesContainer, ghostContainer } = this;
let targetKey = '';
let type = '';
const dragKey = dragData.key;
if (!dragKey) return
const targetNode = nodesContainer.querySelector('.collide-node');
if (targetNode) {
targetKey = this.getKeyByElement(targetNode);
if (targetNode.classList.contains('become-previous')) type = 'previous';
if (targetNode.classList.contains('become-next')) type = 'next';
if (targetNode.classList.contains('become-child')) type = 'child';
}
// 停止拖动,移除拖动效果
this.FollowScroll.stop();
nodesContainer.classList.remove('cursor-move');
ghostContainer.innerHTML = '';
this.removeDragEffect();
this.initDragData();
if (targetNode) {
const from = this.createNodeParams(dragKey);
this.insertNode(targetKey, dragKey, type);
const to = this.createNodeParams(dragKey);
// emit dragEndHook
dragEnd && dragEnd({ from, to, target: targetKey, key: dragKey, type });
}
});
// 处理拖拽过程中滚动的情况
const oldScroll = {
top: container.scrollTop,
left: container.scrollLeft
};
this.registerEvent('scroll', () => {
const { key, ghostElement, ghostTranslateY: oldTranslateY, ghostTranslateX: oldTranslateX } = this.dragData;
const { left: oldScrollLeft, top: oldScrollTop } = oldScroll;
const { scrollLeft: currentScrollLeft, scrollTop: currentScrollTop } = container;
if (key && ghostElement) {
const ghostTranslateX = oldTranslateX + currentScrollLeft - oldScrollLeft;
const ghostTranslateY = oldTranslateY + currentScrollTop - oldScrollTop;
ghostElement.style.transform = `translate(${ghostTranslateX}px, ${ghostTranslateY}px)`;
Object.assign(this.dragData, { ghostTranslateX, ghostTranslateY });
this.setDragEffect(this.getGhostPosition());
}
oldScroll.left = currentScrollLeft;
oldScroll.top = currentScrollTop;
}, container);
this.FollowScroll = new FollowScroll({
scrollContainer: container,
autoScrollTriggerDistance,
eventContainer: nodesContainer,
autoScrollSpeed
});
}
getGhostPosition() {
const { ghostTranslateX, ghostTranslateY, ghostElement } = this.dragData;
return {
left: ghostTranslateX,
top: ghostTranslateY,
right: ghostTranslateX + ghostElement.offsetWidth,
bottom: ghostTranslateY + ghostElement.offsetHeight
}
}
removeDragEffect() {
const { linkContainer, nodesContainer } = this;
const tempLink = linkContainer.querySelector('.is-temp-line');
tempLink && linkContainer.removeChild(tempLink);
const collideNode = nodesContainer.querySelector('.collide-node');
collideNode && collideNode.classList.remove('collide-node', 'become-previous', 'become-next', 'become-child');
const tempChildrenContainer = nodesContainer.querySelector('.temp-children-container');
tempChildrenContainer && tempChildrenContainer.parentElement.removeChild(tempChildrenContainer);
const notAllowEffect = nodesContainer.querySelector('.show-not-allow');
notAllowEffect && notAllowEffect.classList.remove('show-not-allow');
}
getCollideType(collideNodeKey, ghostPosition) {
const { rootNode, isVertical, dragData, positionData } = this;
const { key: dragNodeKey } = dragData;
const { top: collideNodeTop, bottom: collideNodeBottom, left: collideNodeLeft, right: collideNodeRight } = positionData.node[collideNodeKey];
const { left: ghostLeft, right: ghostRight, top: ghostTop, bottom: ghostBottom } = ghostPosition;
const dragNode = this.getNodeElement(dragNodeKey);
const collideNode = this.getNodeElement(collideNodeKey);
// 拖到父节点时只能作为兄弟节点插入
const coverIsParent = collideNodeKey === this.getParentKey(dragNodeKey);
// 禁止插入到上一个兄弟节点的下面
const coverIsPrevious = collideNodeKey === this.getPreviousKey(dragNodeKey);
// 禁止插入到下一个兄弟节点的上面
const coverIsNext = collideNodeKey === this.getNextKey(dragNodeKey);
// 权限数据
const allowConfig = {
child: !collideNode.classList.contains('not-allow-insert-child') && !coverIsParent,
next: !collideNode.classList.contains('not-allow-insert-next') && !coverIsPrevious,
previous: !collideNode.classList.contains('not-allow-insert-previous') && !coverIsNext
};
// 如果被覆盖的是根节点的话只允许作为子节点插入
if (collideNode === rootNode) return allowConfig.child ? 'child' : 'notAllow'
// 不可拖到子节点上
if (dragNode.parentElement.contains(collideNode)) return 'notAllow'
if (isVertical) {
// 位置偏左或者偏右(50%)则认为是添加兄弟节点
const offsetValue = (collideNodeRight - collideNodeLeft) * 0.5;
const leftPositionValue = collideNodeLeft + offsetValue;
const rightPositionValue = collideNodeRight - offsetValue;
// 在左边插入
if (ghostRight <= leftPositionValue && allowConfig.previous) return 'previous'
// 在右边插入
if (ghostLeft >= rightPositionValue && allowConfig.next) return 'next'
} else {
// 位置偏上或者偏下(50%)则认为是添加兄弟节点
const offsetValue = (collideNodeBottom - collideNodeTop) * 0.5;
const topPositionValue = collideNodeTop + offsetValue;
const bottomPositionValue = collideNodeBottom - offsetValue;
// 在上方插入
if (ghostBottom <= topPositionValue && allowConfig.previous) return 'previous'
// 在下方插入
if (ghostTop >= bottomPositionValue && allowConfig.next) return 'next'
}
// 作为子节点插入
if (allowConfig.child) return 'child'
// 不满足自定义控制条件的按照child=>next=>previous的权重取一个
for (const key in allowConfig) {
if (allowConfig[key]) return key
}
return 'notAllow'
}
getCollideLinePosition(collideType, collideNodeKey) {
const { isVertical, positionData, option } = this;
const { distanceX, distanceY } = option;
const linPositionData = {};
const { top: collideNodeTop, bottom: collideNodeBottom, left: collideNodeLeft, right: collideNodeRight } = positionData.node[collideNodeKey];
if (isVertical) {
// 插入子节点类型
if (collideType === 'child') {
linPositionData.from = {
x: (collideNodeLeft + collideNodeRight) / 2,
y: collideNodeBottom,
key: collideNodeKey
};
// 有子节点并且子节点展开
if (this.existChildren(collideNodeKey) && !this.nodeIsFold(collideNodeKey)) {
const lastChildrenNodeKey = this.getChildrenKeys(collideNodeKey).pop();
const { right: lastChildrenNodeRight, top: lastChildrenNodeTop } = positionData.node[lastChildrenNodeKey];
linPositionData.to = {
x: lastChildrenNodeRight + 20,
y: lastChildrenNodeTop,
key: 'temp'
};
return linPositionData
}
// 没有子节点和子节点折叠相同处理
linPositionData.to = {
x: (collideNodeLeft + collideNodeRight) / 2,
y: collideNodeBottom + distanceY,
key: 'temp'
};
return linPositionData
}
const parentNodeKey = this.getParentKey(collideNodeKey);
const parentPosition = positionData.node[parentNodeKey];
linPositionData.from = {
x: (parentPosition.left + parentPosition.right) / 2,
y: parentPosition.bottom,
key: parentNodeKey
};
linPositionData.to = {
x: collideType === 'previous' ? collideNodeLeft - 20 : collideNodeRight + 20,
y: collideNodeTop,
key: 'temp'
};
return linPositionData
}
// 插入子节点类型
if (collideType === 'child') {
linPositionData.from = {
x: collideNodeRight,
y: (collideNodeTop + collideNodeBottom) / 2,
key: collideNodeKey
};
// 有子节点并且子节点展开
if (this.existChildren(collideNodeKey) && !this.nodeIsFold(collideNodeKey)) {
const lastChildrenNodeKey = this.getChildrenKeys(collideNodeKey).pop();
const { left: lastChildrenNodeLeft, bottom: lastChildrenNodeBottom } = positionData.node[lastChildrenNodeKey];
linPositionData.to = {
x: lastChildrenNodeLeft,
y: lastChildrenNodeBottom + 20,
key: 'temp'
};
return linPositionData
}
// 没有子节点和子节点折叠相同处理
linPositionData.to = {
x: collideNodeRight + distanceX,
y: (collideNodeTop + collideNodeBottom) / 2,
key: 'temp'
};
return linPositionData
}
const parentNodeKey = this.getParentKey(collideNodeKey);
const parentPosition = positionData.node[parentNodeKey];
linPositionData.from = {
x: parentPosition.right,
y: (parentPosition.top + parentPosition.bottom) / 2,
key: parentNodeKey
};
linPositionData.to = {
x: collideNodeLeft,
y: collideType === 'previous' ? collideNodeTop - 20 : collideNodeBottom + 20,
key: 'temp'
};
return linPositionData
}
// 生成拖动效果
createDragEffect(collideNodeKey, ghostPosition) {
const { positionData } = this;
const collideNode = this.getNodeElement(collideNodeKey);
const collideType = this.getCollideType(collideNodeKey, ghostPosition);
if (collideType === 'notAllow') return setNotAllowEffect(collideNode)
collideNode.classList.add(`become-${collideType}`, 'collide-node');
const { from, to } = this.getCollideLinePosition(collideType, collideNodeKey);
this.drawLine(from, to, true);
// 插入子节点类型:不存在子节点,或者子节点被收起,这时候需要创建临时节点
if (collideType === 'child' && (!this.existChildren(collideNodeKey) || this.nodeIsFold(collideNodeKey))) {
const { top: collideNodeTop, bottom: collideNodeBottom, left: collideNodeLeft, right: collideNodeRight } = positionData.node[collideNodeKey];
const chartContent = document.createElement('div');
chartContent.classList.add('tree-chart-node', 'temp-chart-content');
chartContent.style.width = `${collideNodeRight - collideNodeLeft}px`;
chartContent.style.height = `${collideNodeBottom - collideNodeTop}px`;
const childrenContainer = this.createChildrenContainer('temp-children-container');
const chartContainer = this.createNodeContainer(true);
chartContainer.appendChild(chartContent);
childrenContainer.appendChild(chartContainer);
collideNode.parentElement.appendChild(childrenContainer);
}
}
// 获取拖动过程中碰撞的元素
getCollideNode({ left: ghostLeft, right: ghostRight, top: ghostTop, bottom: ghostBottom }) {
const { key: draggingKey } = this.dragData;
const {
left: positionLeft,
right: positionRight,
top: positionTop,
bottom: positionBottom,
node: nodePosition
} = this.positionData;
// 寻找符合条件最近的位置
const searchCurrentPosition = (target, sortList, searchLarge) => {
const listLen = sortList.length;
if (searchLarge) {
for (let i = 0; i < listLen; i++) {
if (sortList[i] >= target) return sortList[i]
}
} else {
for (let i = listLen - 1; i > -1; i--) {
if (sortList[i] <= target) return sortList[i]
}
}
return null
};
const currentLeft = searchCurrentPosition(ghostLeft, positionRight.sortList, true);
const currentTop = searchCurrentPosition(ghostTop, positionBottom.sortList, true);
const currentRight = searchCurrentPosition(ghostRight, positionLeft.sortList);
const currentBottom = searchCurrentPosition(ghostBottom, positionTop.sortList);
// 寻找交叉范围内的节点
const getRangeList = (markValue, directPositionList, searchLarge) => {
let result = [];
isNumber(markValue) && directPositionList.sortList.forEach(positionValue => {
if (searchLarge ? positionValue >= markValue : positionValue <= markValue) {
result = result.concat(directPositionList[positionValue]);
}
});
return result
};
// 根据ghost节点左边界和上边界求交集确定在右下方的元素
const leftCatchNodes = getRangeList(currentLeft, positionRight, true);
const topCatchNodes = getRangeList(currentTop, positionBottom, true);
// 根据ghost节点右边界和下边界求交集确定在左上方的元素
const rightCatchNodes = getRangeList(currentRight, positionLeft);
const bottomCatchNodes = getRangeList(currentBottom, positionTop);
// 四个区间求交集确定目标元素
const collideNodes = getArrayIntersection(leftCatchNodes, topCatchNodes, rightCatchNodes, bottomCatchNodes);
// 忽略自身
const draggingNodeIndex = collideNodes.indexOf(draggingKey);
draggingNodeIndex > -1 && collideNodes.splice(draggingNodeIndex, 1);
if (!collideNodes.length) return null
if (collideNodes.length === 1) return collideNodes[0]
// 如果存在多个被覆盖的节点需要根据覆盖面积决策,取覆盖面最大的节点
const maxAreaNode = { nodeKey: '', nodeArea: 0 };
const getArea = nodeKey => {
const { left: nodeLeft, right: nodeRight, top: nodeTop, bottom: nodeBottom } = nodePosition[nodeKey];
const x = nodeLeft <= ghostLeft ? nodeRight - ghostLeft : ghostRight - nodeLeft;
const y = nodeTop <= ghostTop ? nodeBottom - ghostTop : ghostBottom - nodeTop;
return x * y
};
collideNodes.forEach(nodeKey => {
const nodeArea = getArea(nodeKey);
nodeArea > maxAreaNode.nodeArea && Object.assign(maxAreaNode, { nodeKey, nodeArea });
});
return maxAreaNode.nodeKey
}
setDragEffect(ghostElementPosition) {
this.removeDragEffect();
const collideNodeKey = this.getCollideNode(ghostElementPosition);
collideNodeKey && this.createDragEffect(collideNodeKey, ghostElementPosition);
}
resize() {
const { nodesContainer, linkContainer, draggable, ghostContainer, rootNode, isVertical } = this;
const { style: nodesContainerStyle } = nodesContainer;
const { style: linkContainerStyle } = linkContainer;
// 需要先清除旧的尺寸
nodesContainerStyle.height = 'auto';
nodesContainerStyle.width = 'auto';
nodesContainerStyle.minHeight = 'auto';
nodesContainerStyle.minWidth = 'auto';
const { clientWidth: nodeContainerWidth, clientHeight: nodeContainerHeight } = nodesContainer;
let width = nodeContainerWidth;
let height = nodeContainerHeight;
// 可拖拽时需要扩展宽度或高度,这样才能插入新节点
if (draggable) {
const { width: rootNodeWidth, height: rootNodeHeight } = rootNode.getBoundingClientRect();
if (isVertical) {
height += rootNodeHeight;
} else {
width += rootNodeWidth;
}
}
const newWidth = `${width}px`;
const newHeight = `${height}px`;
nodesContainerStyle[isVertical ? 'minHeight' : 'minWidth'] = '100%';
nodesContainerStyle.width = linkContainerStyle.width = newWidth;
nodesContainerStyle.height = linkContainerStyle.height = newHeight;
if (draggable) {
const { style: ghostContainerStyle } = ghostContainer;
ghostContainerStyle.width = newWidth;
ghostContainerStyle.height = newHeight;
}
}
setDragScroll() {
const { container, nodesContainer } = this;
let lock = true;
const getEventNode = target => {
if (target.classList.contains('tree-chart-node')) return target
let searchElement = target;
while (nodesContainer !== searchElement) {
if (searchElement.classList.contains('tree-chart-node')) return searchElement
searchElement = searchElement.parentElement;
}
return null
};
nodesContainer.addEventListener('mousedown', e => {
if (e.button !== 0 || getEventNode(e.target)) return
lock = false;
});
nodesContainer.addEventListener('mousemove', e => {
e.preventDefault();
if (e.button !== 0 || lock) return
container.scrollLeft = container.scrollLeft - e.movementX;
container.scrollTop = container.scrollTop - e.movementY;
});
this.registerEvent('mouseup', e => {
if (e.button !== 0) return
lock = true;
});
}
destroy() {
this.FollowScroll.destroy();
this.unregisterEvent();
this.container.innerHTML = '';
for (const key in this) {
this[key] = null;
}
}
}
const data = {
name: 1,
id: 1,
children: [
{
name: 11,
id: 11,
children: [{ name: 111, id: 111 }]
},
{
name: 12,
id: 12,
children: [
{ name: 121, id: 121 },
{
name: 122,
id: 122,
children: [{ name: 1221, id: 1221 }, { name: 1222, id: 1222 }, { name: 1223, id: 1223 }]
}
]
},
{
id: 13,
name: 13,
children: [{
name: 131,
id: 131,
children: [
{
name: 1311,
id: 1311,
children: [{ name: 13111, id: 13111 }]
}]
}]
},
{
id: 14,
name: 14,
children: [{ name: 141, id: 141 }]
}
]
};
const chart = new TreeChart({
data,
container: document.querySelector('#demo'),
// isVertical: false,
distanceX: 80,
distanceY: 80,
draggable: true,
dragScroll: true,
autoScrollTriggerDistance: 20,
allowFold: true,
foldNodeKeys: ['122'],
// line: {
// type: 'broken'
// },
nodeControl(data) {
return {
draggable: data.id !== 12,
insertChild: data.id !== 12,
insertPrevious: data.id !== 12,
insertNext: data.id !== 12
}
},
dragStart() {
console.log('dragstart');
},
dragEnd(data) {
console.log('dragend', data);
},
click() {
console.log('click');
},
// mouseEnter(data) {
// console.log('enter', data)
// },
// mouseLeave(data) {
// console.log('leave', data)
// },
contentRender(data) {
const container = document.createElement('div');
const { style: containerStyle } = container;
containerStyle.width = '230px';
containerStyle.height = '80px';
containerStyle.border = 'solid 1px';
containerStyle.lineHeight = '80px';
container.innerText = data.name;
return container
}
});
document.querySelector('.re-render').addEventListener('click', () => {
chart.reRender(data);
});
document.querySelector('.re-render-node').addEventListener('click', () => {
chart.reRenderNode('122', { name: 999, id: 999 });
});
document.querySelector('.remove-node').addEventListener('click', () => {
chart.removeNode('11');
});
document.querySelector('.move-node').addEventListener('click', () => {
chart.insertNode('12', '111', 'child');
});
document.querySelector('.insert-child').addEventListener('click', () => {
chart.insertNode('12', { name: 'insertChild', id: 'insertChild' }, 'child');
});
document.querySelector('.insert-previous').addEventListener('click', () => {
chart.insertNode('12', { name: 'insertPrevious', id: 'insertPrevious' }, 'previous');
});
document.querySelector('.insert-next').addEventListener('click', () => {
chart.insertNode('12', { name: 'insertNext', id: 'insertNext' }, 'next');
});
document.querySelector('.destroy').addEventListener('click', () => {
chart.destroy();
});
}());
function e(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function t(e,t){for(var n=0;n<t.length;n++){var i=t[n];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(e,i.key,i)}}function n(e,n,i){return n&&t(e.prototype,n),i&&t(e,i),e}function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function o(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?r(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):r(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}var a=function(){function t(n){e(this,t);var i=n.scrollContainer,r=n.eventContainer,o=n.autoScrollTriggerDistance,a=n.autoScrollSpeed;this.scrollContainer=i,this.eventContainer=r,this.autoScrollSpeed=a,this.triggerDistance=o||0,this.targetNode=null,this.interval=0,this.directData={left:!1,right:!1,top:!1,bottom:!1},this.setEvent()}return n(t,[{key:"setEvent",value:function(){var e=this,t=this.eventContainer;this.mouseMoveHandler=function(t){var n=t.button,i=t.movementX,r=t.movementY;0===i&&0===r||!n&&e.targetNode&&(e.setDirectData(t),e.triggerScroll())},t.addEventListener("mousemove",this.mouseMoveHandler)}},{key:"setDirectData",value:function(e){var t=e.movementX,n=e.movementY,i=this.scrollContainer,r=this.targetNode,o=this.triggerDistance,a=this.directData,s=i.scrollLeft,l=i.scrollTop,c=i.scrollWidth,d=i.scrollHeight,h=i.clientWidth,u=i.clientHeight,v=i.getBoundingClientRect(),g=v.left,f=v.right,y=v.top,p=v.bottom,m=r.getBoundingClientRect(),C=m.left,k=m.right,E=m.top,L=m.bottom;C-g<o&&(a.left=!0),E-y<o&&(a.top=!0),f-k<o&&(a.right=!0),p-L<o&&(a.bottom=!0),(t>0||0===s)&&(a.left=!1),(n>0||0===l)&&(a.top=!1),(t<0||s+h>=c)&&(a.right=!1),(n<0||l+u>=d)&&(a.bottom=!1)}},{key:"triggerScroll",value:function(){var e=this.directData,t=this.scrollContainer,n=this.autoScrollSpeed,i=!1;for(var r in e)if(e[r]){i=!0;break}if(!i)return this.stop(!0);this.interval||(this.interval=setInterval((function(){var i=t.scrollLeft,r=t.scrollTop;e.left&&(i-=n),e.right&&(i+=n),e.top&&(r-=n),e.bottom&&(r+=n),t.scrollLeft=i,t.scrollTop=r}),20))}},{key:"start",value:function(e){this.targetNode=e}},{key:"stop",value:function(){var e=arguments.length>0&&void 0!==arguments[0]&&arguments[0];e||(this.targetNode=null),this.interval&&(clearInterval(this.interval),this.interval=0)}},{key:"destroy",value:function(){this.eventContainer.removeEventListener("mousemove",this.mouseMoveHandler),this.targetNode=this.scrollContainer=this.eventContainer=null,this.interval=0}}]),t}(),s=function(e){return/HTML/.test(Object.prototype.toString.call(e))&&1===e.nodeType},l=function(e){return/Number/.test(Object.prototype.toString.call(e))},c=function(){function t(n){e(this,t),this.mergeOption(n),this.createChartElement(),this.resize(),this.setEvent()}return n(t,[{key:"getKeyByElement",value:function(e){return s(e)&&e.classList.contains("tree-chart-node")?e.getAttribute("data-key"):null}},{key:"getNodeElement",value:function(e){return this.nodesContainer.querySelector(".tree-chart-item-".concat(e))}},{key:"getPreviousKey",value:function(e){try{var t=this.getNodeElement(e);return this.getKeyByElement(t.parentElement.previousElementSibling.querySelector(".tree-chart-node"))}catch(e){return null}}},{key:"getNextKey",value:function(e){try{var t=this.getNodeElement(e);return this.getKeyByElement(t.parentElement.nextElementSibling.querySelector(".tree-chart-node"))}catch(e){return null}}},{key:"getParentKey",value:function(e){try{var t=this.getNodeElement(e);return this.getKeyByElement(t.parentElement.parentElement.previousElementSibling)}catch(e){return null}}},{key:"getChildrenKeys",value:function(e){var t=this.getNodeElement(e).getAttribute("data-children");return t?t.split(","):[]}},{key:"existChildren",value:function(e){var t=this.getNodeElement(e);return!!t&&Boolean(t.getAttribute("data-children"))}},{key:"insertNode",value:function(e,t,n){var i=this.getNodeElement(e);if(!/next|previous/.test(n)||i!==this.rootNode){var r=null,o=null,a=null;if(/Object/.test(Object.prototype.toString.call(t)))r=this.getKeyField(t),o=this.createNode(t),(a=this.createNodeContainer()).appendChild(o),this.setNodeEvent(o);else{r=t,a=(o=this.getNodeElement(r)).parentElement;var s=this.getParentKey(r);this.removeChildrenKey(s,r),this.existChildren(s)||(this.removeChildrenContainer(s),this.allowFold&&this.removeFoldButton(s))}if("child"===n){if(this.existChildren(e))this.getChildrenContainer(e).appendChild(a),this.nodeIsFold(e)&&this.toggleFold(e);else{var l=this.createChildrenContainer();l.appendChild(a),i.parentElement.appendChild(l),this.createFoldButton(i)}this.addChildrenKey(e,r)}else{var c=this.getParentKey(e),d=this.getChildrenContainer(c),h=i.parentElement;"previous"===n&&d.insertBefore(a,h),"next"===n&&d.insertBefore(a,h.nextElementSibling),this.addChildrenKey(c,r)}this.reloadLink()}}},{key:"removeNode",value:function(e){var t=this.getNodeElement(e);if(t){if(t===this.rootNode)return console.warn("not allow remove root node");var n=this.getParentKey(e);this.removeChildrenKey(n,e);var i=t.parentElement;i.parentElement.removeChild(i),this.existChildren(n)||(this.removeChildrenContainer(n),this.allowFold&&this.removeFoldButton(n)),this.reloadLink()}}},{key:"nodeIsFold",value:function(e){return this.getFoldButton(e).classList.contains("is-fold")}},{key:"toggleFold",value:function(e){var t=this.getFoldButton(e);if(t){var n=this.getChildrenContainer(e);this.nodeIsFold(e)?(n.classList.remove("is-hidden"),t.classList.remove("is-fold")):(n.classList.add("is-hidden"),t.classList.add("is-fold")),this.reloadLink()}}},{key:"reRenderNode",value:function(e,t){var n=this,i=e.toString(),r=this.getKeyField(t),o=this.getNodeElement(i),a=this.getChildrenKeys(e);if(r!==i){var s=this.linkContainer,l=this.getParentKey(i);this.replaceChildrenKey(l,i,r);var c="line-".concat(l,"-").concat(i),d=s.querySelector(".".concat(c));d.classList.remove(c),d.classList.add("line-".concat(l,"-").concat(r)),a.forEach((function(e){var t="line-".concat(i,"-").concat(e),o=n.linkContainer.querySelector(".".concat(t));o.classList.remove(t),o.classList.add("line-".concat(r,"-").concat(e))})),this.replacePositionNodeKey(i,r)}var h=this.createNode(t);a.length&&h.setAttribute("data-children",a.join()),o.querySelector(".tree-chart-unfold")&&this.createFoldButton(h);var u=o.parentElement;u.insertBefore(h,o),u.removeChild(o)}},{key:"reloadLink",value:function(){this.createLink(),this.resize()}},{key:"reRender",value:function(e){var t=this.nodesContainer;t.innerHTML="";var n=this.createNodeContainer();t.appendChild(n),this.createNodes(e,n,!0),this.reloadLink()}},{key:"registerEvent",value:function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:window;this.eventList||(this.eventList=[]);var i=t.bind(this);this.eventList.push({eventTarget:n,type:e,handler:i}),n.addEventListener(e,i)}},{key:"unregisterEvent",value:function(){this.eventList.forEach((function(e){return e.eventTarget.removeEventListener(e.type,e.handler)}))}},{key:"addChildrenKey",value:function(e,t){var n=this.getNodeElement(e),i=n.getAttribute("data-children")||"";n.setAttribute("data-children",i?"".concat(i,",").concat(t):t)}},{key:"removeChildrenKey",value:function(e,t){var n=this.getChildrenKeys(e);if(n.length){var i=n.indexOf(t);i>-1&&n.splice(i,1);var r=this.getNodeElement(e);n.length?r.setAttribute("data-children",n.join()):r.removeAttribute("data-children")}}},{key:"replaceChildrenKey",value:function(e,t,n){var i=this.getChildrenKeys(e);if(i.length){var r=i.indexOf(t);-1!==r&&(i.splice(r,1,n),this.getNodeElement(e).setAttribute("data-children",i.join()))}}},{key:"createChildrenContainer",value:function(e){var t=document.createElement("div");return t.classList.add("tree-chart-children-container"),e&&t.classList.add(e),t}},{key:"getChildrenContainer",value:function(e){return this.getNodeElement(e).nextElementSibling}},{key:"removeChildrenContainer",value:function(e){var t=this.getChildrenContainer(e);t.parentElement.removeChild(t)}},{key:"getKeyField",value:function(e){return e[this.option.keyField].toString()||null}}]),n(t,[{key:"mergeOption",value:function(e){var t=o({keyField:"id",isVertical:!0,distanceX:40,distanceY:40,allowFold:!1,foldNodeKeys:[],draggable:!1,dragScroll:!1,autoScrollTriggerDistance:50,autoScrollSpeed:6,line:{type:"bezier",smooth:50}},e),n=t.draggable,i=t.allowFold,r=t.dragScroll,a=t.isVertical,s=t.line,c=t.padding,d=t.distanceX,h=t.distanceY;d<40&&(t.distanceX=40),h<40&&(t.distanceY=40),this.draggable=n,this.allowFold=i,this.dragScroll=r,this.isVertical=a,this.lineConfig=s;var u=o({top:30,right:30,bottom:30,left:30},c);for(var v in u){var g=u[v];l(g)||(g=0),n&&g<30&&(g=30),u[v]=g}this.containerPadding=u,this.option=t,this.initHooks()}},{key:"initHooks",value:function(){var e=this,t=["contentRender"].concat(["nodeControl","preventDrag"],["dragStart","dragEnd","click","mouseEnter","mouseLeave"]);this.hooks={},t.forEach((function(t){var n=e.option[t];"function"==typeof n&&(e.hooks[t]=n)}))}},{key:"createChartElement",value:function(){var e=this.isVertical,t=this.option,n=t.container,i=t.data;n.classList.add("tree-chart"),e&&n.classList.add("is-vertical"),this.container=n,this.createNodes(i,this.createNodesContainer()),this.createLinkContainer(),this.createLink(),this.createGhostContainer()}},{key:"createNodesContainer",value:function(){var e=this.container,t=this.containerPadding,n=this.createNodeContainer();for(var i in n.classList.add("is-nodes-container"),t)if(/left|top|right|bottom/.test(i)){var r=t[i];n.style["padding".concat(i.replace(/^./,(function(e){return e.toUpperCase()})))]="".concat(r,"px")}e.appendChild(n),this.nodesContainer=n;var o=this.createNodeContainer();return n.append(o),o}},{key:"createNodeContainer",value:function(){var e=document.createElement("div");return e.classList.add("tree-chart-container"),e}},{key:"createNodes",value:function(e,t,n){var i=this,r=this.allowFold,o=this.rootNode,a=this.option,s=e.children,l=a.foldNodeKeys,c=Array.isArray(s)&&s.length>0,d=this.createNode(e);if(t.appendChild(d),o&&!n||(this.rootNode=d),c){var h=this.createChildrenContainer();if(t.appendChild(h),r){var u=this.createFoldButton(d);l.includes(this.getKeyField(e))&&(u.classList.add("is-fold"),h.classList.add("is-hidden"))}var v=[];s.forEach((function(e){v.push(i.getKeyField(e));var t=i.createNodeContainer();h.appendChild(t),i.createNodes(e,t)})),d.setAttribute("data-children",v.join())}}},{key:"createNode",value:function(e){var t=this.draggable,n=this.hooks,i=this.option,r=i.distanceX,a=i.distanceY,l=n.contentRender,c=n.nodeControl,d=document.createElement("div"),h=this.getKeyField(e);d.classList.add("tree-chart-node","tree-chart-item-".concat(h)),d.setAttribute("data-key",h),d.style.marginBottom="".concat(a,"px"),d.style.marginRight="".concat(r,"px");var u=document.createElement("div");if(u.classList.add("tree-render-container"),l){var v=l(e);"string"==typeof v?u.innerHTML=v.replace(/>\s+</g,"><"):s(v)?u.appendChild(v):u.innerText="Please check contentRender return type is string or element"}else u.innerText="option.contentRender is required";if(d.appendChild(u),this.setNodeEvent(d),t&&c){var g=o({draggable:!0,insertChild:!0,insertPrevious:!0,insertNext:!0},c(e));!g.draggable&&d.classList.add("not-allow-drag"),!g.insertChild&&d.classList.add("not-allow-insert-child"),!g.insertPrevious&&d.classList.add("not-allow-insert-previous"),!g.insertNext&&d.classList.add("not-allow-insert-next")}return d}},{key:"createGhostContainer",value:function(){var e=this.container,t=document.createElement("div");t.classList.add("tree-chart-ghost-container"),e.appendChild(t),this.ghostContainer=t}},{key:"createFoldButton",value:function(e){if(!e.querySelector(".tree-chart-unfold")){var t=document.createElement("div");return t.classList.add("tree-chart-unfold"),t.innerHTML="<div></div><div></div>",e.appendChild(t),t}}},{key:"getFoldButton",value:function(e){return this.getNodeElement(e).querySelector(".tree-chart-unfold")}},{key:"removeFoldButton",value:function(e){var t=this.getFoldButton(e);t&&t.parentElement.removeChild(t)}},{key:"createLinkContainer",value:function(){var e=this.container,t=document.createElementNS("http://www.w3.org/2000/svg","svg");t.classList.add("tree-chart-link-container"),e.appendChild(t),this.linkContainer=t}},{key:"createLink",value:function(){var e=this,t=this.container,n=this.nodesContainer,i=this.linkContainer,r=this.draggable,o=this.isVertical,a=t.getBoundingClientRect(),s=a.left,l=a.top,c=t.scrollLeft,d=t.scrollTop;i.innerHTML="",r&&this.initPositionData(),n.querySelectorAll(".tree-chart-node").forEach((function(t){var n=t.getAttribute("data-children"),i=t.getBoundingClientRect(),a=i.left,h=i.right,u=i.top,v=i.bottom,g=e.getKeyByElement(t),f=t.nextElementSibling;if(n&&!f.classList.contains("is-hidden")){var y={x:a-s+c+(o?t.offsetWidth/2:t.offsetWidth),y:u-l+d+(o?t.offsetHeight:t.offsetHeight/2),key:g};n.split(",").forEach((function(t){var n=e.getNodeElement(t),i=n.getBoundingClientRect(),r=i.left,a=i.top,h=n.offsetWidth,u=(n.offsetHeight,{x:r-s+c+(o?h/2:0),y:a-l+d+(o?0:n.offsetHeight/2),key:t});e.drawLine(y,u)}))}r&&e.addPositionData(g,{left:a-s+c,right:h-s+c,top:u-l+d,bottom:v-l+d})}))}},{key:"initPositionData",value:function(){this.positionData={left:{sortList:[]},right:{sortList:[]},top:{sortList:[]},bottom:{sortList:[]},node:{}}}},{key:"addPositionData",value:function(e,t){var n=this.positionData;for(var i in n.node[e]=t,t){var r=t[i],o=n[i];o[r]?o[r].push(e):o[r]=[e];var a=o.sortList;if(a.length)for(var s=0,l=a.length;s<l;s++){var c=a[s];if(r===c)break;if(r<c){a.splice(s,0,r);break}s===l-1&&a.push(r)}else a.push(r)}}},{key:"replacePositionNodeKey",value:function(e,t){var n=this.positionData;n.node[t]=n.node[e],delete n.node[e],["left","top","right","bottom"].forEach((function(i){var r=n[i];for(var o in r)if("sortList"!==o){var a=r[o],s=a.indexOf(e);if(s>-1){a.splice(s,1,t);break}}}))}},{key:"setEvent",value:function(){var e=this.allowFold,t=this.draggable,n=this.dragScroll;e&&this.setFoldEvent(),this.setClickHook(),t&&this.setDragEvent(),n&&this.setDragScroll()}},{key:"setFoldEvent",value:function(){var e=this;this.nodesContainer.addEventListener("click",(function(t){var n=t.target;n.classList.contains("tree-chart-unfold")&&e.toggleFold(e.getKeyByElement(n.parentElement))}))}},{key:"drawLine",value:function(e,t,n){var i=this.linkContainer,r=this.isVertical,o=this.allowFold,a=this.lineConfig,s=a.type,l=a.smooth,c="line-".concat(e.key,"-").concat(t.key),d=document.querySelector(".".concat(c));d||((d=document.createElementNS("http://www.w3.org/2000/svg","path")).classList.add(c,"line-from-".concat(e.key)),n&&d.classList.add("is-temp-line"),i.appendChild(d));var h="",u=e.x,v=e.y,g=t.x,f=t.y;o&&this.existChildren(e.key)&&(r?v+=5:u+=5);var y=(g-u)/2,p=(f-v)/2,m="".concat(u," ").concat(v),C="".concat(g," ").concat(f);if("straight"===s&&(h="M".concat(m," T ").concat(C)),"broken"===s){var k="",E="";r?(k="".concat(u," ").concat(v+p),E="".concat(g," ").concat(f-p)):(k="".concat(u+y," ").concat(v),E="".concat(g-y," ").concat(f)),h="M".concat(m," L").concat(k," L").concat(E," L").concat(C)}if("bezier"===s){var L=l/100,b="";b=r?"".concat(u," ").concat(v+(p-p*L)):"".concat(u+(y-y*L)," ").concat(v);var N="".concat(u+y," ").concat(v+p);h="M".concat(m," Q").concat(b," ").concat(N," T ").concat(C)}d.setAttribute("d",h)}},{key:"getCurrentEventNode",value:function(e){var t=this.nodesContainer;if(e.classList.contains("tree-chart-unfold"))return null;for(var n=e;t!==n;){if(n.classList.contains("tree-chart-node"))return n;n=n.parentElement}return null}},{key:"isDragging",value:function(){var e=this.draggable,t=this.dragData;if(!e)return!1;var n=t.ghostTranslateX,i=t.ghostTranslateY;return t.key&&(0!==n||0!==i)}},{key:"setClickHook",value:function(){var e=this,t=this.nodesContainer,n=this.hooks.click;if(n){var i=null;t.addEventListener("mousedown",(function(t){var n=t.button,r=t.target;0===n&&(i=e.getCurrentEventNode(r))})),t.addEventListener("mouseup",(function(t){var r=t.button,o=t.target;if(0===r){var a=e.getCurrentEventNode(o);a&&a===i&&!e.isDragging()&&n({key:e.getKeyByElement(a),element:a},t)}}))}}},{key:"setNodeEvent",value:function(e){var t=this,n=this.hooks,i=n.mouseEnter,r=n.mouseLeave,o={key:this.getKeyByElement(e),element:e},a=e.querySelector(".tree-render-container");i&&a.addEventListener("mouseenter",(function(e){t.isDragging()||i(o,e)})),r&&a.addEventListener("mouseleave",(function(e){t.isDragging()||r(o,e)}))}},{key:"createNodeParams",value:function(e){return{key:e,parentKey:this.getParentKey(e),previousKey:this.getPreviousKey(e),nextKey:this.getNextKey(e)}}},{key:"initDragData",value:function(){this.dragData={key:null,ghostElement:null,ghostTranslateX:0,ghostTranslateY:0,mouseDownOffsetX:0,mouseDownOffsetY:0}}},{key:"setDragEvent",value:function(){var e=this,t=this.ghostContainer,n=this.container,i=this.nodesContainer,r=this.hooks,o=this.option,s=o.autoScrollTriggerDistance,l=o.autoScrollSpeed,c=r.preventDrag,d=r.dragStart,h=r.dragEnd,u=!0;this.initDragData(),i.addEventListener("mousedown",(function(t){var i=t.button,r=t.clientX,o=t.clientY;if(0===i){var a=e.getCurrentEventNode(t.target),s=e.getKeyByElement(a);if(a&&a!==e.rootNode&&!(a.classList.contains("not-allow-drag")||c&&c({key:s,element:a},t))){var l=e.positionData.node[s],d=l.left,h=l.top,u=a.cloneNode(!0);u.style.margin="0px",Object.assign(e.dragData,{key:s,ghostElement:u,mouseDownOffsetX:r+n.scrollLeft-d,mouseDownOffsetY:o+n.scrollTop-h}),e.FollowScroll.start(u)}}})),i.addEventListener("mousemove",(function(r){var o=r.button,a=r.movementX,s=r.movementY,l=r.clientX,c=r.clientY,h=e.dragData,v=h.ghostElement,g=h.mouseDownOffsetX,f=h.mouseDownOffsetY,y=h.key;if(0===o&&y&&(0!==a||0!==s)){getSelection&&getSelection().removeAllRanges(),i.classList.add("cursor-move"),!t.contains(v)&&t.appendChild(v);var p=l+n.scrollLeft-g,m=c+n.scrollTop-f;v.style.transform="translate(".concat(p,"px, ").concat(m,"px)"),Object.assign(e.dragData,{ghostTranslateX:p,ghostTranslateY:m});var C=e.getGhostPosition();e.setDragEffect(C),d&&u&&(u=!1,d({key:y,element:e.getNodeElement(y)}))}})),this.registerEvent("mouseup",(function(){u=!0;var t=e.dragData,n=e.nodesContainer,i=e.ghostContainer,r="",o="",a=t.key;if(a){var s=n.querySelector(".collide-node");if(s&&(r=e.getKeyByElement(s),s.classList.contains("become-previous")&&(o="previous"),s.classList.contains("become-next")&&(o="next"),s.classList.contains("become-child")&&(o="child")),e.FollowScroll.stop(),n.classList.remove("cursor-move"),i.innerHTML="",e.removeDragEffect(),e.initDragData(),s){var l=e.createNodeParams(a);e.insertNode(r,a,o);var c=e.createNodeParams(a);h&&h({from:l,to:c,target:r,key:a,type:o})}}}));var v={top:n.scrollTop,left:n.scrollLeft};this.registerEvent("scroll",(function(){var t=e.dragData,i=t.key,r=t.ghostElement,o=t.ghostTranslateY,a=t.ghostTranslateX,s=v.left,l=v.top,c=n.scrollLeft,d=n.scrollTop;if(i&&r){var h=a+c-s,u=o+d-l;r.style.transform="translate(".concat(h,"px, ").concat(u,"px)"),Object.assign(e.dragData,{ghostTranslateX:h,ghostTranslateY:u}),e.setDragEffect(e.getGhostPosition())}v.left=c,v.top=d}),n),this.FollowScroll=new a({scrollContainer:n,autoScrollTriggerDistance:s,eventContainer:i,autoScrollSpeed:l})}},{key:"getGhostPosition",value:function(){var e=this.dragData,t=e.ghostTranslateX,n=e.ghostTranslateY,i=e.ghostElement;return{left:t,top:n,right:t+i.offsetWidth,bottom:n+i.offsetHeight}}},{key:"removeDragEffect",value:function(){var e=this.linkContainer,t=this.nodesContainer,n=e.querySelector(".is-temp-line");n&&e.removeChild(n);var i=t.querySelector(".collide-node");i&&i.classList.remove("collide-node","become-previous","become-next","become-child");var r=t.querySelector(".temp-children-container");r&&r.parentElement.removeChild(r);var o=t.querySelector(".show-not-allow");o&&o.classList.remove("show-not-allow")}},{key:"getCollideType",value:function(e,t){var n=this.rootNode,i=this.isVertical,r=this.dragData,o=this.positionData,a=r.key,s=o.node[e],l=s.top,c=s.bottom,d=s.left,h=s.right,u=t.left,v=t.right,g=t.top,f=t.bottom,y=this.getNodeElement(a),p=this.getNodeElement(e),m=e===this.getParentKey(a),C=e===this.getPreviousKey(a),k=e===this.getNextKey(a),E={child:!p.classList.contains("not-allow-insert-child")&&!m,next:!p.classList.contains("not-allow-insert-next")&&!C,previous:!p.classList.contains("not-allow-insert-previous")&&!k};if(p===n)return E.child?"child":"notAllow";if(y.parentElement.contains(p))return"notAllow";if(i){var L=.5*(h-d);if(v<=d+L&&E.previous)return"previous";if(u>=h-L&&E.next)return"next"}else{var b=.5*(c-l);if(f<=l+b&&E.previous)return"previous";if(g>=c-b&&E.next)return"next"}if(E.child)return"child";for(var N in E)if(E[N])return N;return"notAllow"}},{key:"getCollideLinePosition",value:function(e,t){var n=this.isVertical,i=this.positionData,r=this.option,o=r.distanceX,a=r.distanceY,s={},l=i.node[t],c=l.top,d=l.bottom,h=l.left,u=l.right;if(n){if("child"===e){if(s.from={x:(h+u)/2,y:d,key:t},this.existChildren(t)&&!this.nodeIsFold(t)){var v=this.getChildrenKeys(t).pop(),g=i.node[v],f=g.right,y=g.top;return s.to={x:f+20,y:y,key:"temp"},s}return s.to={x:(h+u)/2,y:d+a,key:"temp"},s}var p=this.getParentKey(t),m=i.node[p];return s.from={x:(m.left+m.right)/2,y:m.bottom,key:p},s.to={x:"previous"===e?h-20:u+20,y:c,key:"temp"},s}if("child"===e){if(s.from={x:u,y:(c+d)/2,key:t},this.existChildren(t)&&!this.nodeIsFold(t)){var C=this.getChildrenKeys(t).pop(),k=i.node[C],E=k.left,L=k.bottom;return s.to={x:E,y:L+20,key:"temp"},s}return s.to={x:u+o,y:(c+d)/2,key:"temp"},s}var b=this.getParentKey(t),N=i.node[b];return s.from={x:N.right,y:(N.top+N.bottom)/2,key:b},s.to={x:h,y:"previous"===e?c-20:d+20,key:"temp"},s}},{key:"createDragEffect",value:function(e,t){var n=this.positionData,i=this.getNodeElement(e),r=this.getCollideType(e,t);if("notAllow"===r)return i.classList.add("show-not-allow");i.classList.add("become-".concat(r),"collide-node");var o=this.getCollideLinePosition(r,e),a=o.from,s=o.to;if(this.drawLine(a,s,!0),"child"===r&&(!this.existChildren(e)||this.nodeIsFold(e))){var l=n.node[e],c=l.top,d=l.bottom,h=l.left,u=l.right,v=document.createElement("div");v.classList.add("tree-chart-node","temp-chart-content"),v.style.width="".concat(u-h,"px"),v.style.height="".concat(d-c,"px");var g=this.createChildrenContainer("temp-children-container"),f=this.createNodeContainer(!0);f.appendChild(v),g.appendChild(f),i.parentElement.appendChild(g)}}},{key:"getCollideNode",value:function(e){var t=e.left,n=e.right,i=e.top,r=e.bottom,o=this.dragData.key,a=this.positionData,s=a.left,c=a.right,d=a.top,h=a.bottom,u=a.node,v=function(e,t,n){var i=t.length;if(n){for(var r=0;r<i;r++)if(t[r]>=e)return t[r]}else for(var o=i-1;o>-1;o--)if(t[o]<=e)return t[o];return null},g=v(t,c.sortList,!0),f=v(i,h.sortList,!0),y=v(n,s.sortList),p=v(r,d.sortList),m=function(e,t,n){var i=[];return l(e)&&t.sortList.forEach((function(r){(n?r>=e:r<=e)&&(i=i.concat(t[r]))})),i},C=function(){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];var i=t.length;if(i<2)return[];var r=[],o={};return t.reduce((function(e,t){return e.concat(t)}),[]).forEach((function(e){o[e]?(o[e]++,o[e]===i&&r.push(e)):o[e]=1})),r}(m(g,c,!0),m(f,h,!0),m(y,s),m(p,d)),k=C.indexOf(o);if(k>-1&&C.splice(k,1),!C.length)return null;if(1===C.length)return C[0];var E={nodeKey:"",nodeArea:0};return C.forEach((function(e){var o=function(e){var o=u[e],a=o.left,s=o.right,l=o.top,c=o.bottom;return(a<=t?s-t:n-a)*(l<=i?c-i:r-l)}(e);o>E.nodeArea&&Object.assign(E,{nodeKey:e,nodeArea:o})})),E.nodeKey}},{key:"setDragEffect",value:function(e){this.removeDragEffect();var t=this.getCollideNode(e);t&&this.createDragEffect(t,e)}},{key:"resize",value:function(){var e=this.nodesContainer,t=this.linkContainer,n=this.draggable,i=this.ghostContainer,r=this.rootNode,o=this.isVertical,a=e.style,s=t.style;a.height="auto",a.width="auto",a.minHeight="auto",a.minWidth="auto";var l=e.clientWidth,c=e.clientHeight;if(n){var d=r.getBoundingClientRect(),h=d.width,u=d.height;o?c+=u:l+=h}var v="".concat(l,"px"),g="".concat(c,"px");if(a[o?"minHeight":"minWidth"]="100%",a.width=s.width=v,a.height=s.height=g,n){var f=i.style;f.width=v,f.height=g}}},{key:"setDragScroll",value:function(){var e=this.container,t=this.nodesContainer,n=!0;t.addEventListener("mousedown",(function(e){0!==e.button||function(e){if(e.classList.contains("tree-chart-node"))return e;for(var n=e;t!==n;){if(n.classList.contains("tree-chart-node"))return n;n=n.parentElement}return null}(e.target)||(n=!1)})),t.addEventListener("mousemove",(function(t){t.preventDefault(),0!==t.button||n||(e.scrollLeft=e.scrollLeft-t.movementX,e.scrollTop=e.scrollTop-t.movementY)})),this.registerEvent("mouseup",(function(e){0===e.button&&(n=!0)}))}},{key:"destroy",value:function(){for(var e in this.FollowScroll.destroy(),this.unregisterEvent(),this.container.innerHTML="",this)this[e]=null}}]),t}();export default c;

2

package.json
{
"name": "treechartjs",
"version": "0.0.1",
"version": "0.0.2",
"description": "",

@@ -5,0 +5,0 @@ "main": "dist/index.js",

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc