UNPKG

oda-framework

Version:

It's an ES Progressive Framework based on the technology of Web Components and designed especially for creating custom UI/UX of any complexity for web and cross-platform PWA mobile applications.

441 lines (439 loc) 19.1 kB
// import '../lib/vis-network/dist/vis-network.js'; ODA({ is: 'oda-network', imports: '../lib/vis-network/dist/vis-network.js', template: /*html*/` <style> :host { @apply --flex; height: 100%; position: relative; background: white; } :host > div { @apply --flex; height: 100%; position: relative; background: white; } </style> <div id="network-div"></div> `, _getNewItemId() { return this.itemId++; }, $public: { get networkDiv() { return this.$('#network-div') }, get rootItem() { return this.items?.[0]; }, dataSet: { $type: Array }, items: { $type: Array, get() { const array = Object.assign([], this.dataSet); let expanding = (data, level) => { let children = data.filter(i => i[this.parentKey] && i[this.idKey] !== i[this.parentKey]); children.forEach(i => { let item = data.find(p => p[this.idKey] === i[this.parentKey]); delete i[this.parentKey]; if (item) { item.items = item.items || []; if (!item.items.includes(i)) item.items.push(i); } else { data.push(i); // children.splice(children.indexOf(i),1); } data.splice(data.indexOf(i), 1); }); let expands = data.filter(i => { i.id = (typeof i.id !== 'undefined') ? i.id : this._getNewItemId(); if (!i.expanded) { this.fire('expand', i); i.expanded = true; } return i.expanded && i.items && i.items.length; }); level++; expands.forEach(e => { let idx = array.indexOf(e); array.splice(idx + 1, 0, ...e.items); expanding(e.items, level); }); }; if (array.length > 0) expanding(array, 0); return array; }, set(items) { [...this.nodes._data.values()].forEach(node => { if (!items.some(i => i.id === node.id)) this.nodes.remove(node); }); let lines = []; items.forEach(i => { if (i.items && i.items.length > 0) { i.items.forEach(s => { lines.push({ id: s.id, from: i.id, to: s.id }); }) } }); Object.values(this.edges._data).forEach(edge => { if (!items.some(i => i.id === edge.id)) this.edges.remove(edge); }); items.forEach(i => { if (!this.nodes._data.has(i.id)) { this.nodes.add(i); } this.nodes.update(i); }); lines.forEach(i => { if (!this.edges._data.has(i.id)) this.edges.add({ id: i.id, from: i.from, to: i.to }); }); if (this.network && !this.focusedItem) { this.network.setData({ nodes: this.nodes, edges: this.edges }); let clusterOptionsByData = { processProperties: (clusterOptions, childNodes) => { clusterOptions.label = "[ Cluster " + childNodes.length + " ]"; return clusterOptions; }, clusterNodeProperties: { borderWidth: 2, shape: 'box', font: { size: 30 } } }; this.network.clusterByHubsize(undefined, clusterOptionsByData); this.network.stabilize(); } } }, // options: { // $type: Object, // set(options) { // if (this.network) this.network.setOptions(options); // } // }, focusedItem: { $type: Object }, physics: { $type: Boolean, $def: true, set(physics) { if (this.network) this.network.setOptions({ physics: { enabled: physics } }); } }, networkEvents: { $type: Array, $def: ['click', 'doubleClick', 'oncontext', 'dragStart', 'dragging', 'dragEnd', 'zoom', 'showPopup', 'hidePopup', 'select', 'selectNode', 'selectEdge', 'deselectNode', 'deselectEdge', 'hoverNode', 'hoverEdge', 'blurNode', 'blurEdge', 'startStabilizing', 'stabilizationProgress', 'stabilizationIterationsDone', 'stabilized', /*'resize',*/ 'initRedraw', 'beforeDrawing', 'afterDrawing', 'animationFinished', 'configChange'] }, network: { $type: Object, set(network, old) { if (old) { this.networkEvents.forEach(e => this.network.off(e)); } if (network) { this.networkEvents.forEach(e => { this.network.on(e, detail => { if (detail && detail.nodes && detail.nodes.length) { detail.nodes.forEach(id => { detail.id = id; this.fire(e, detail); }); } else { this.fire(e, detail); } }); }); } } }, nodes: { $type: Object }, edges: { $type: Object }, itemId: 0, refreshCount: 0, idKey: 'id', parentKey: 'parent' }, $listeners: { 'dragStart': 'onDragStart', 'dragging': 'onDragging', 'dragEnd': 'onDragEnd', 'selectNode': 'onSelectNode', 'deselectNode': 'onDeselectNode', 'doubleClick': 'onDoubleClick', 'contextmenu': 'onContextmenu', 'oncontext': 'onContextmenu', 'resize': 'onResize', 'stabilizationIterationsDone': 'onStabilizationIterationsDone' }, onDragStart(event, d) { if (this.network && event.detail.value.nodes && event.detail.value.nodes.length > 0) { if (this.network.isCluster(event.detail.value.nodes[0]) === true) { this.physics = true; this.network.openCluster(event.detail.value.nodes[0]); // this.network.stabilize(); } else { this.network.setOptions({ nodes: { physics: false } }); this.focusedItem = this.items.find(i => i.id === event.detail.value.id); } } }, onDragging(event, d) { if (event.detail.value.nodes && event.detail.value.nodes.length) { const weight = 20; const positions = this.network.getPositions(); for (let i in positions) { if ( positions.hasOwnProperty(i) && Math.abs(event.detail.value.pointer.canvas.x - positions[i].x) < weight && Math.abs(event.detail.value.pointer.canvas.y - positions[i].y) < weight && parseInt(i) !== event.detail.value.id ) { this.network.selectNodes([event.detail.value.id, parseInt(i)]); } else { this.network.selectNodes([event.detail.value.id]); } } } }, onDragEnd(event, d) { if (this.network && event.detail.value.nodes && event.detail.value.nodes.length) this.network.setOptions({ nodes: { physics: true } }); }, onSelectNode(event, d) { if (this.network && event.detail.value.nodes && event.detail.value.nodes.length > 0) { if (this.network.isCluster(event.detail.value.nodes[0]) === true) { this.physics = true; this.network.openCluster(event.detail.value.nodes[0]); // this.network.stabilize(); } else { this.focusedItem = this.items.find(i => i.id === event.detail.value.id); /*if( this.focusedItem && this.focusedItem.links && this.focusedItem.links.length > 0 ) { if( this.network ) this.network.setOptions( { nodes: { physics: false } } ); this.focusedItem.links.forEach( link => { let toItem = this.items.find( toItem => toItem.item.Root[ this.idKey ] === link ); this.edges.add( { from: this.focusedItem.id, to: toItem.id, arrows: 'to', dashes: true } ); } ); }*/ // if (this.focusedItem.item instanceof odaObject && // this.focusedItem.item.Root.$gant && // this.focusedItem.item.Root.$gant[0].$dependens && // this.focusedItem.item.Root.$gant[0].$dependens.length > 0) { // this.network.setOptions({ nodes: { physics: false } }); // this.focusedItem.item.Root.$gant[0].$dependens.forEach(depend => { // let toItem = this.items.find(toItem => toItem.item.Root[this.idKey] === depend.link); // if (!Object.values(this.edges._data).some(edge => edge.from === this.focusedItem.id && edge.to === toItem.id)) // this.edges.add({ // from: this.focusedItem.id, // to: toItem.id, // arrows: 'to', // dashes: true // }); // }); // } this.camOnNode(this.focusedItem.id); } } }, onDeselectNode(event, d) { Object.values(this.edges._data).filter(edge => !Number.isInteger(edge.id)).forEach(edge => { this.edges.remove(edge); }); if (this.focusedItem && event.detail.value.nodes && !event.detail.value.nodes.length && this.focusedItem.id !== this.rootItem.id) { this.focusedItem = this.items.find(i => i.id === this._getParentNode(this.focusedItem).id); this.camOnNode(this.focusedItem.id); this.network.selectNodes([this.focusedItem.id]); } else if (this.focusedItem && event.detail.value.nodes && !event.detail.value.nodes.length && this.focusedItem.id === this.rootItem.id) { this.focusedItem = null; this.camOnNode(0, 0.5); } }, onDoubleClick(event, d) { if (event.detail.value.nodes && event.detail.value.nodes.length && this.focusedItem.item instanceof odaObject) { this.focusedItem.item.show(); } else if (event.detail.value.nodes && event.detail.value.nodes.length && this.focusedItem.item instanceof odaStorage) { this.focusedItem.item.navigate(); } }, async onContextmenu(event, d) { if (event.detail.value?.pointer) { const weight = 20; const positions = this.network.getPositions(); for (let i in positions) { if ( positions.hasOwnProperty(i) && Math.abs(event.detail.value.pointer.canvas.x - positions[i].x) < weight && Math.abs(event.detail.value.pointer.canvas.y - positions[i].y) < weight ) { const nodeId = parseInt(i); const item = this.nodes._data.get(nodeId).item; item.showMenu({ contextItem: item }); if(item) { this.listen('event', '_onEvent', { target: item }); this.focusedItem = this.items.find(item => item.id === nodeId); } this.focusNode(nodeId); } } } }, onResize(e, d) { if (this.network) this.network.setOptions({ height: '100%' }); //this.network.stabilize(); }, onStabilizationIterationsDone() { this.physics = false; }, _onEvent(event, d) { switch (event.source) { //create before-save saved after-save update changed change case 'changed': let changedItem = this.items.find(mapItem => mapItem.item === event.detail.value); if (changedItem) { if (event.detail.value instanceof odaObject) { changedItem.label = event.detail.value.name || event.detail.value.oid; } else { changedItem.label = event.detail.value.label; } this.refresh(); } break; case 'create': let curItem = event.currentTarget; let addItem = event.detail.value; let mapItem = this.items.find(mapItem => mapItem.item === curItem) || this.rootItem; let item = addItem instanceof odaObject ? { item: addItem, label: addItem.name || addItem.oid, p: addItem.p, oid: addItem.oid, shape: 'dot', group: curItem.id } : { item: addItem, label: addItem.label, items: [], group: addItem.id }; item.id = this._getNewItemId(); mapItem.items = mapItem.items || []; mapItem.items.push(item); this.refresh(); break; case 'deleted': if (event.currentTarget) { let mapItem = this.items.find(mapItem => mapItem.item === event.currentTarget); if (!mapItem || mapItem === this.rootItem) return; let removingChildren = items => { items.forEach((i, idx) => { if (i.items && i.items.length) { removingChildren(i.items); } items.splice(idx, 1); }); }; if (mapItem && mapItem.items && mapItem.items.length) { removingChildren(mapItem.items); } let removingItems = items => { items.forEach((i, idx) => { if (i.id === mapItem.id) { items.splice(idx, 1); } else { if (i.items && i.items.length === 0 && i.expanded) { i.expanded = false; } if (i.items && i.items.length) { removingItems(i.items); } } }); }; removingItems(this.items); } this.refresh(); break; } }, attached() { this.nodes ??= new vis.DataSet([]); this.edges ??= new vis.DataSet([]); let data = { nodes: this.nodes, edges: this.edges }; let opts = { //width: (this.offsetWidth) + 'px', //height: this.offsetHeight + 'px' // height: '878px' }; const options = { locale: navigator.language.substr(0, 2), nodes: { shape: 'box', shadow: true, color: { border: 'black', background: 'white' }, size: 10, margin: 10 }, layout: { randomSeed: 8 }, /*clustering: { enabled: false, clusterEdgeThreshold: 50 },*/ physics: { stabilization: { enabled: true, iterations: 1000 }, barnesHut: { gravitationalConstant: -60000, springConstant: 0.02 }, solver: 'barnesHut', // adaptiveTimestep: false, // timestep: 1 }, // smoothCurves: {dynamic:false}, // zoomExtentOnStabilize: true, interaction: { hideEdgesOnDrag: true, keyboard: true //dragNodes: false } }; Object.assign(opts, options); this.network = new vis.Network(this.networkDiv, data, opts); }, _getParentNode(node) { const nodeId = typeof node === 'object' ? node.id : node || node; let parentEdge = [...this.edges._data.values()].find(edge => edge.to === nodeId); return [...this.nodes._data.values()].find(node => node.id === parentEdge.from); }, focusNode(node = 0) { const nodeId = typeof node === 'object' ? node.id : node || node; this.network.selectNodes([nodeId]); }, camOnNode(node = 0, scale) { const nodeId = typeof node === 'object' ? node.id : node || node; const calcScale = scale ? scale : (this.nodes._data.get(nodeId).items && this.nodes._data.get(nodeId).items.length > 1) ? 2 - (1 - 1 / this.nodes._data.get(nodeId).items.length) * 1.5 : 2; const options = { animation: { duration: 500, }, scale: calcScale, }; this.network.focus(nodeId, options); }, refresh() { this.debounce('refresh', () => { this.refreshCount++; }, 10); } });