pipicar/public/js/tree-diagram.js

437 lines
16 KiB
JavaScript

(function () {
this.treeDiagram = function (selector, data, options) {
let defaults = {
itemsWidth: null,
fullScreenContainer: false,
lineColor: '#333333',
customItemStyle: false,
toggleGroups: {
show: false,
customClass: 'toggle-group-button',
customStyle: false
},
draggable: {
show: false,
customClass: 'reset-draggable',
customStyle: false,
resetButtonText: 'Reset drag',
insertElement: {
position: 'beforeend',
element: 'body'
}
},
zoom: {
show: false,
default: 1,
customClass: 'buttons-zoom-container',
customStyle: false,
zoomInText: 'Zoom in',
zoomOutText: 'Zoom out',
insertElement: {
position: 'beforeend',
element: 'body'
}
}
};
this.settings = (arguments[2] && typeof arguments[2] === 'object') ? extendDefaults(defaults, arguments[2]) : defaults;
this.init(selector, data, options);
};
/*** Public Methods */
treeDiagram.prototype.init = function (selector, data) {
this.container = document.querySelector(selector);
this.diagram = this.container.firstElementChild;
this.treeBlock = this.diagram.firstElementChild;
this.treeItem = this.treeBlock.firstElementChild;
this.treeBlock.remove();
this.data = data;
const createDone = create.call(this);
if (createDone) {
if (this.settings.toggleGroups.show) {
initToggleButtons.call(this);
}
initStyles.call(this);
insertStyles.call(this);
if (this.settings.draggable.show) {
initDraggable.call(this);
}
if (this.settings.zoom.show) {
initZoom.call(this);
}
}
};
function create() {
this.diagram.insertAdjacentElement("beforeend", createTreeBlock.call(this, this.data));
if (this.data.hasOwnProperty('children')) {
recursiveCreate.call(this, this.data['children'], this.data['id']);
}
return true;
}
/*** Private Methods */
function createTreeBlock(obj) {
const resultHtml = this.treeBlock.cloneNode(true);
resultHtml.setAttribute('data-id', obj['id']);
for (let key in obj) {
if (key !== 'id' && key !== 'children') {
const el = resultHtml.querySelector(`[data-field="${key}"]`);
if (el) {
if (el.tagName === 'IMG') {
el.src = obj[key];
} else if (el.tagName === 'A') {
if (typeof obj[key] === 'object') {
const items = Object.keys(obj[key]);
el.innerHTML = obj[key][items[0]];
el.href = obj[key][items[1]];
} else {
el.href = obj[key];
}
} else {
el.innerHTML = obj[key];
}
}
}
}
return resultHtml;
}
function recursiveCreate(array, id) {
const treeGroupEl = document.createElement('div');
const treeBlockParent = this.diagram.querySelector(`[data-id="${id}"]`);
const treeItem = treeBlockParent.firstElementChild;
treeGroupEl.classList = 'tree-diagram-group show';
treeGroupEl.setAttribute('data-group', id);
treeItem.classList.add('show-group');
if (array.length > 0) {
treeItem.classList.add('has-children');
}
if (array.length > 1) {
treeItem.classList.add('has-children-more-one');
treeGroupEl.classList.add('multi-line');
}
if (this.settings.toggleGroups.show) {
initToggleButton.call(this, id);
}
const parentDiagram = treeBlockParent.appendChild(treeGroupEl);
array.forEach(item => {
parentDiagram.insertAdjacentElement("beforeend", createTreeBlock.call(this, item));
if (item.hasOwnProperty('children')) {
recursiveCreate.call(this, item['children'], item.id);
}
});
}
function initToggleButton(id) {
const treeBlockParent = this.diagram.querySelector(`[data-id="${id}"]`);
const treeItem = treeBlockParent.firstElementChild;
const toggleButton = document.createElement('span');
toggleButton.classList = `${this.settings.toggleGroups.customClass} show`;
toggleButton.setAttribute('data-toggle', id);
treeItem.appendChild(toggleButton);
}
function initStyles() {
this.style = document.createElement('style');
this.head = document.head || document.getElementsByTagName('head')[0];
const classDiagramContainer = this.container.classList.value;
const classDiagram = this.diagram.classList.value;
const classTreeBlock = this.treeBlock.classList.value;
const classTreeItem = this.treeItem.classList.value;
let widthTreeItem = null;
if(this.settings.itemsWidth === null){
widthTreeItem = 'auto';
} else {
widthTreeItem = this.settings.itemsWidth + 'px';
}
console.log(widthTreeItem);
this.style.textContent = `
.${classDiagramContainer}{
overflow: hidden;
}
.${classDiagram} {
position: relative;
text-align: center;
transition: background-color 0.2s ease;
}
.${classDiagram}.draggable{
background-color: #f2f2f2;
}
.${classDiagram} > .${classTreeBlock} > .${classTreeItem}::after{
content: none;
}
.${classTreeItem} {
position: relative;
display: inline-block;
width: ${widthTreeItem};
}
.${classTreeItem}::after {
content: '';
position: absolute;
top: -21px;
left: 50%;
transform: translateX(-50%);
height: 20px;
width: 1px;
background-color: ${this.settings.lineColor};
}
.${classTreeItem}.has-children::before {
content: '';
position: absolute;
bottom: -41px;
height: 40px;
left: 50%;
transform: translateX(-50%);
width: 1px;
background-color: ${this.settings.lineColor};
}
.${classTreeItem}.show-group::before{
bottom: -61px;
height: 60px;
}
.${classTreeBlock} {
display: inline-block;
margin-left: 20px;
}
.${classTreeBlock}:first-child {
margin-left: 0;
}
.tree-diagram-group {
display: flex;
align-items: flex-start;
margin-top: 60px;
padding-top: 20px;
position: relative;
visibility: hidden;
opacity: 0;
transition: 0.3s all ease;
}
.tree-diagram-group.show{
visibility: visible;
opacity: 1;
}
.tree-diagram-group.multi-line::before {
content: '';
position: absolute;
top: 0;
height: 1px;
background-color: ${this.settings.lineColor};
}
`;
if (!this.settings.customItemStyle) {
const itemStyle = `
.${classTreeItem} {
padding: 20px;
border-radius: 6px;
border: 1px solid #ddd;
background-color: #ddd;
}`;
this.style.insertAdjacentText('beforeend', itemStyle);
}
if (!this.settings.toggleGroups.customStyle) {
const toggleGroupButton = `
.${classTreeItem} .${this.settings.toggleGroups.customClass}{
position: absolute;
bottom: -45px;
left: 50%;
transform: translateX(-50%);
width: 30px;
height: 30px;
border-radius: 50%;
background-color: #333;
cursor: pointer;
}
.${classTreeItem} .${this.settings.toggleGroups.customClass}::before{
content: '+';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 25px;
line-height: 1;
color: #fff;
}
.${classTreeItem} .${this.settings.toggleGroups.customClass}.show::before{
content: '-';
top: calc(50% - 2px);
}`;
this.style.insertAdjacentText('beforeend', toggleGroupButton);
}
this.head.appendChild(this.style);
}
function insertStyles() {
const elementsOfGroup = this.diagram.querySelectorAll('[data-group]');
if (elementsOfGroup) {
elementsOfGroup.forEach(item => {
const childItem = item.querySelectorAll(`.${this.treeBlock.classList.value}`);
const dataGroup = item.getAttribute('data-group');
let widthLeft = this.settings.itemsWidth / 2;
let widthRight = this.settings.itemsWidth / 2;
if (childItem.length > 0) {
widthLeft = (childItem[0].offsetWidth / 2);
widthRight = childItem[childItem.length - 1].offsetWidth / 2;
}
const treeGroupBefore = `
.tree-diagram-group[data-group="${dataGroup}"].multi-line::before{
left: ${widthLeft}px;
right: ${widthRight}px;
}`;
this.style.insertAdjacentText('beforeend', treeGroupBefore);
this.head.appendChild(this.style);
});
}
}
function initToggleButtons() {
const toggleButtons = this.diagram.querySelectorAll('[data-toggle]');
toggleButtons.forEach(button => {
const id = button.getAttribute('data-toggle');
const group = this.diagram.querySelector(`[data-group='${id}']`);
button.addEventListener('click', function (event) {
group.classList.toggle('show');
button.parentElement.classList.toggle('show-group');
button.classList.toggle('show');
});
});
}
function initZoom() {
const diagram = this.diagram;
const buttonsZoomContainer = document.createElement('div');
const buttonZoomIn = document.createElement('button');
const buttonZoomOut = document.createElement('button');
buttonsZoomContainer.classList = `${this.settings.zoom.customClass}`;
buttonZoomIn.classList = 'zoom-in';
buttonZoomOut.classList = 'zoom-out';
buttonZoomIn.textContent = this.settings.zoom.zoomInText;
buttonZoomOut.textContent = this.settings.zoom.zoomOutText;
buttonsZoomContainer.insertAdjacentElement('beforeend', buttonZoomIn);
buttonsZoomContainer.insertAdjacentElement('beforeend', buttonZoomOut);
document.querySelector(this.settings.zoom.insertElement.element).insertAdjacentElement(this.settings.zoom.insertElement.position, buttonsZoomContainer);
diagram.style.zoom = this.settings.zoom.default;
document.body.querySelector(`.${this.settings.zoom.customClass} .zoom-in`).addEventListener('click', function () {
diagram.style.zoom = +diagram.style.zoom + 0.12;
});
document.body.querySelector(`.${this.settings.zoom.customClass} .zoom-out`).addEventListener('click', function () {
diagram.style.zoom = +diagram.style.zoom - 0.12;
});
if (!this.settings.zoom.customStyle && this.settings.fullScreenContainer) {
const style = `
.${this.settings.zoom.customClass}{
position: fixed;
bottom: 20px;
right: 20px;
z-index: 1001;
}`;
this.style.insertAdjacentText('beforeend', style);
}
}
function initDraggable() {
const diagramContainer = this.container;
const diagram = this.diagram;
const resetButtonBlock = document.createElement('div');
const resetButton = document.createElement('button');
resetButtonBlock.classList = this.settings.draggable.customClass;
resetButton.textContent = this.settings.draggable.resetButtonText;
resetButtonBlock.insertAdjacentElement('beforeend', resetButton);
document.querySelector(this.settings.draggable.insertElement.element).insertAdjacentElement(this.settings.draggable.insertElement.position, resetButtonBlock);
diagram.style.width = diagram.scrollWidth + 'px';
if (this.settings.fullScreenContainer) {
diagramContainer.style.width = window.innerWidth + 'px';
diagramContainer.style.height = window.innerHeight + 'px';
diagramContainer.style.overflow = 'hidden';
window.addEventListener('resize', function () {
diagramContainer.style.width = window.innerWidth + 'px';
diagramContainer.style.height = window.innerHeight + 'px';
});
}
let isDrag = false;
let xDrag = 0;
let yDrag = 0;
function startDrag(event) {
isDrag = true;
xDrag = event.clientX - diagram.offsetLeft;
yDrag = event.clientY - diagram.offsetTop;
}
function stop_drag() {
isDrag = false;
diagram.classList.remove('draggable');
}
function while_drag(event) {
diagramContainer.addEventListener('mouseleave', () => {
return stop_drag();
});
if (isDrag) {
event.cancelBubble = true;
event.returnValue = false;
diagram.style.left = (event.clientX - xDrag) + 'px';
diagram.style.top = (event.clientY - yDrag) + 'px';
diagram.classList.add('draggable');
}
}
document.body.querySelector(`.${this.settings.draggable.customClass} button`).addEventListener('click', function () {
diagram.style.left = 0;
diagram.style.top = 0;
});
diagramContainer.addEventListener('mousedown', startDrag);
diagramContainer.addEventListener('mousemove', while_drag);
diagramContainer.addEventListener('mouseup', stop_drag);
if (!this.settings.draggable.customStyle && this.settings.fullScreenContainer) {
const style = `
.${this.settings.draggable.customClass}{
position: fixed;
bottom: 50px;
right: 20px;
z-index: 1001;
}`;
this.style.insertAdjacentText('beforeend', style);
}
}
function extendDefaults(defaults, properties) {
Object.keys(properties).forEach(property => {
if (properties.hasOwnProperty(property)) {
defaults[property] = properties[property];
}
});
return defaults;
};
}());