UNPKG

orgchart

Version:

Simple and direct organization chart(tree-like hierarchy) library based on pure DOM.

197 lines (182 loc) 7.08 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>OrgChart Filter Node Demo</title> <link rel="icon" href="img/logo.png"> <link rel="stylesheet" href="css/orgchart.css"> <link rel="stylesheet" href="css/style.css"> <style type="text/css"> .orgchart .node.matched { background-color: rgba(238, 217, 54, 0.5); } .orgchart .hierarchy.first-shown::before { left: calc(50% - 1px); width: calc(50% + 1px); } .orgchart .hierarchy.last-shown::before { width: calc(50% + 1px); } .orgchart .hierarchy.first-shown.last-shown::before { width: 2px; } #edit-panel { text-align: center; margin: 0.5rem; padding: 0.5rem; border: 1px solid #aaa; } #edit-panel * { font-size: 1rem; } button, input { padding: 0.5rem 1rem; } </style> </head> <body> <section class="demo-notes"> <h1>Filter Node Demo</h1> <p>This demo filters nodes by keyword and highlights the visible path that leads to each match.</p> </section> <div id="chart-container"></div> <div id="edit-panel" class="view-state"> <input type="text" id="key-word"> <button type="button" id="btn-filter-node">Filter</button> <button type="button" id="btn-cancel">Cancel</button> </div> <script type="text/javascript" src="js/orgchart.js"></script> <script type="text/javascript"> function loopChart(hierarchyElement) { const nodesLayer = hierarchyElement.querySelector(':scope > .nodes'); const siblings = nodesLayer ? Array.from(nodesLayer.children).filter((child) => child.classList.contains('hierarchy')) : []; const visibleSiblings = siblings.filter((sibling) => !sibling.classList.contains('hidden')); if (visibleSiblings.length) { visibleSiblings[0].classList.add('first-shown'); visibleSiblings[visibleSiblings.length - 1].classList.add('last-shown'); } siblings.forEach((sibling) => loopChart(sibling)); } function filterNodes(keyWord) { if (!keyWord.length) { window.alert('Please type key word firstly.'); return; } const chart = document.querySelector('.orgchart'); if (!chart) { return; } chart.classList.add('noncollapsable'); const nodes = Array.from(chart.querySelectorAll('.node')); const matchedNodes = nodes.filter((node) => node.textContent.toLowerCase().indexOf(keyWord) > -1); matchedNodes.forEach((node) => { node.classList.add('matched'); let hierarchy = node.closest('.hierarchy'); while (hierarchy) { const parentNodes = hierarchy.parentElement; const parentHierarchy = parentNodes ? parentNodes.closest('.hierarchy') : null; if (!parentHierarchy) { break; } const parentNode = parentHierarchy.querySelector(':scope > .node'); if (parentNode) { parentNode.classList.add('retained'); } hierarchy = parentHierarchy; } }); Array.from(chart.querySelectorAll('.matched, .retained')).forEach((node) => { node.classList.remove('slide-up'); const currentHierarchy = node.closest('.hierarchy'); const currentLayer = currentHierarchy ? currentHierarchy.parentElement : null; if (currentLayer && currentLayer.classList.contains('nodes')) { currentLayer.classList.remove('hidden'); const parentHierarchy = currentLayer.closest('.hierarchy'); if (parentHierarchy) { parentHierarchy.classList.remove('isChildrenCollapsed'); } } if (currentLayer) { Array.from(currentLayer.children).forEach((sibling) => { if (sibling !== currentHierarchy) { const siblingNode = sibling.querySelector(':scope > .node'); if (siblingNode && !siblingNode.classList.contains('matched') && !siblingNode.classList.contains('retained')) { sibling.classList.add('hidden'); } } }); } }); matchedNodes.forEach((node) => { const childLayer = node.parentElement.querySelector(':scope > .nodes'); if (childLayer && !childLayer.querySelector('.matched')) { childLayer.classList.add('hidden'); node.parentElement.classList.add('isChildrenCollapsed'); } }); const rootHierarchy = chart.querySelector('.hierarchy'); if (rootHierarchy) { loopChart(rootHierarchy); } } function clearFilterResult() { const chart = document.querySelector('.orgchart'); if (!chart) { return; } chart.classList.remove('noncollapsable'); chart.querySelectorAll('.node').forEach((node) => node.classList.remove('matched', 'retained')); chart.querySelectorAll('.hidden, .isChildrenCollapsed, .first-shown, .last-shown').forEach((element) => { element.classList.remove('hidden', 'isChildrenCollapsed', 'first-shown', 'last-shown'); }); chart.querySelectorAll('.slide-up, .slide-left, .slide-right').forEach((element) => { element.classList.remove('slide-up', 'slide-right', 'slide-left'); }); } document.addEventListener('DOMContentLoaded', () => { const datasource = { 'name': 'Lao Lao', 'title': 'general manager', 'children': [ { 'name': 'Bo Miao', 'title': 'department manager', 'children': [ { 'name': 'Li Jing', 'title': 'senior engineer' }, { 'name': 'Li Xin', 'title': 'senior engineer', 'children': [ { 'name': 'To To', 'title': 'engineer' }, { 'name': 'Fei Fei', 'title': 'engineer' }, { 'name': 'Xuan Xuan', 'title': 'engineer' } ] } ] }, { 'name': 'Su Miao', 'title': 'department manager', 'children': [ { 'name': 'Pang Pang', 'title': 'senior engineer' }, { 'name': 'Hei Hei', 'title': 'senior engineer', 'children': [ { 'name': 'Xiang Xiang', 'title': 'UE engineer' }, { 'name': 'Dan Dan', 'title': 'engineer' }, { 'name': 'Zai Zai', 'title': 'engineer' } ] } ] } ] }; const oc = new OrgChart({ chartContainer: '#chart-container', 'data' : datasource, 'nodeContent': 'title' }); document.getElementById('btn-filter-node').addEventListener('click', () => { filterNodes(document.getElementById('key-word').value); }); document.getElementById('btn-cancel').addEventListener('click', () => { clearFilterResult(); }); document.getElementById('key-word').addEventListener('keyup', (event) => { if (event.key === 'Enter') { filterNodes(event.currentTarget.value); } else if (event.key === 'Backspace' && event.currentTarget.value.length === 0) { clearFilterResult(); } }); }); </script> </body> </html>