BALKANGraph / OrgChartJS

OrgChart JS is a simple, flexible and highly customizable organization chart plugin for presenting the structure of your organization and the relationships in an elegant way.
https://balkan.app/orgchartjs
250 stars 85 forks source link

Filtering with Search #817

Closed george-seed closed 8 months ago

george-seed commented 8 months ago

Currently the filter menu let's you untick checkboxes to filter out parts of the tree. That's great, but what I'd like is to allow users to filter the tree using a text input box. For example, when the user starts to type the department (CoE in my example), all other parts of the chart will filter out automatically.

To give you an idea of what I mean

image

image

I've implemented a function to filter the menu, but what I'd like is for the actual chart to start filtering as I am typing.

So if I was to type 'Business' as per the example image, the three departments that have Business in the name would remain ticked (and visible) but other departments would become unticked (and not visible due to my filtering template).

Here is how I am currently searching the menu. It's quite hacky and doesn't actually affect the chart beyond the menu. With this implementation, the user needs to untick everything using the 'All' menu item, then once they've searched for the department they want, tick those departments.

            // Update event handler for filter UI
            chart.filterUI.on('update', function(sender, args){
                var filterSearch = document.getElementById('filterSearch');
                if (filterSearch) {
                    filterSearch.addEventListener('input', function(e){
                        var searchValue = e.target.value.toLowerCase();

                        // Directly targeting div elements inside fieldset within sender.element
                        var filterItems = sender.element.querySelectorAll('fieldset > div');

                        if (filterItems.length > 0) {
                            filterItems.forEach(function(item){
                                var label = item.querySelector('label').textContent.toLowerCase();
                                if (label.includes(searchValue)) {
                                    item.style.display = '';
                                } else {
                                    item.style.display = 'none';
                                }
                            });
                        } else {
                            console.log('No filter items found');
                        }
                    });
                }
            }

If anything isn't clear please let me know and I'll provide more info.

ZornitsaPesheva commented 8 months ago

Hi George,

Could you please create a an example of what you already have?

george-seed commented 8 months ago

The functionality can be seen here:

https://code.balkan.app/filter-search-example#JS

Instructions:

The code is doing this

image

But I want it to

So to look at this from a user perspective, if we imagine an Org-chart representing a large organisation. As a user of the chart, I want to be able to search for a team using a search bar, and have the chart apply my filter template to all nodes not in this team (eg. nodes without the 'Team A' in the team field) based on my search query.

So if I typed 'Team A' as I was typing the filter would be applying to the chart and removing all nodes not in 'Team A'

The important code is below as well:

// Modifying the 'add-filter' event to include a search bar
chart.filterUI.on('add-filter', function(sender, args){
    var names = Object.keys(sender.filterBy);
    var index = names.indexOf(args.name);
    if (index == 0) {  // Adding search bar at the top
        args.html += `<div class="filter-search">
                        <input type="text" id="filterSearch" placeholder="Search filters...">
                        </div>`;
    }
    if (index == names.length - 1) {
        args.html += `<div data-btn-reset style="color: #039BE5;">reset</div>`;
    }
});

// Update event handler for filter UI
chart.filterUI.on('update', function(sender, args){
    var filterSearch = document.getElementById('filterSearch');
    if (filterSearch) {
        filterSearch.addEventListener('input', function(e){
            var searchValue = e.target.value.toLowerCase();

            // Directly targeting div elements inside fieldset within sender.element
            var filterItems = sender.element.querySelectorAll('fieldset > div');

            if (filterItems.length > 0) {
                filterItems.forEach(function(item){
                    var label = item.querySelector('label').textContent.toLowerCase();
                    if (label.includes(searchValue)) {
                        item.style.display = '';
                    } else {
                        item.style.display = 'none';
                    }
                });
            } else {
                console.log('No filter items found');
            }
        });
    }
    var btnResetElement = sender.element.querySelector('[data-btn-reset]');
    if (btnResetElement) {
        btnResetElement.addEventListener('click', function(e){
            sender.filterBy = null;
            sender.update();
            sender.instance.draw();
        });
    }
});
george-seed commented 8 months ago

@ZornitsaPesheva To clarify the approach I've chosen here is just my first attempt.

The goal is to get the following functionality

That way a user can access the org chart, search/filter by Team A and see all members of Team A and nothing else. This is achievable currently by manually expanding the entire tree, then manually selecting the filter you want. I want to automate this process using a search bar.

If there is a better way to achieve this then please let me know. I have recently purchase the org-chart and I am keen to get maximum value from it.

plamen-peshev commented 8 months ago

I have modified the code https://code.balkan.app/filtering-with-search#JS

Let me know if this what you need to achieve

george-seed commented 8 months ago

This is almost exactly what I want, the only small difference is I was trying to get the nodes to expand as well. With the current implementation if the tree is collapsed, then searching for say 'Ava' will just result in a blank tree, as the 'Ava' node is currently collapsed.

image

Initially I was solving this by just expanding all nodes but this wasn't ideal for the end user. So I was hoping there would be a way to expand the nodes being searched/filtered as well as applying the filter template.

I had hoped the below would work but it doesn't.

  // Collect IDs of nodes that match the search value
  chart.nodes.forEach(function(node) {
      if (node.name && node.name.toLowerCase().includes(searchValue)) {
          nodesToExpand.push(node.id);
      }
  });

  // Assuming '1' is the root node ID
  // Expand nodes that match the search value
  chart.expand(1, nodesToExpand);

Any suggestions on what I'm doing wrong?

plamen-peshev commented 8 months ago

There is a way to expand the nodes being searched/filtered but not in "input" listener, because if the user search for "ava" he has to type "a" first and all nodes that countians "a" will expand, it would be better to search/filter on Enter key press or to have separate search/filter button

george-seed commented 8 months ago

If I wanted to implement this, what approach I should use?

For me, chart.expand did not seem to work so I suspect I was using it incorrectly.

chart.expand(1, nodesToExpand);

george-seed commented 8 months ago

I've ended up just going for a simpler button press solution (where the fitler only applies on button press and expands once the input text is submitted.

Thanks for the help on this. Happy to close.