react-complex-tree icon indicating copy to clipboard operation
react-complex-tree copied to clipboard

Search whole tree

Open marchingband opened this issue 2 years ago • 5 comments
trafficstars

The search bar does not search collapsed parts of the tree. I would like an optiion to search the whole tree, and automatically expand any parents to show all the matching items.

marchingband avatar Oct 16 '23 16:10 marchingband

Searching through closed items isn't really the scope of the search bar, it's mostly intended as an accessibility feature to more quickly traverse the tree DOM. Finding a specific item in the tree structure depends on the way how the tree data is structured, and this structure can vary greatly between use cases.

However, I added a function "tree.expandSubsequently" that can be used to expand a path of folders, to be able to more easily expand to a certain item. With that, and custom search implementation, something like this shouldn't be that hard to implement. I built a sample in the documentation, alongside an explanation of the idea: https://rct.lukasbach.com/docs/guides/search#finding-items-that-are-not-loaded-in

lukasbach avatar Nov 27 '23 23:11 lukasbach

I manually wrote a workaround, that essentially does this. My 2cents is that the ideal API would be to optionally search the whole tree and auto expand any branches that contain matches, and then as the search changes, close any branches that were auto-opened, should they no longer contain a match ... this is what I built, as it works for my personal use case, for accessibility, and I am just guessing that it would be a common one. I will look at what you added, thank you very much! I appreciate the response and effort!

marchingband avatar Nov 27 '23 23:11 marchingband

Yes I agree. I might add a more direct interface for directly finding items in the future, but as I mentioned the kind of data structure can vary by use case, and the user can essentially provide an incomplete tree, though I guess this would still be doable with the onMissingItems handler. But maybe this new handler makes building a custom search easy enough.

lukasbach avatar Nov 27 '23 23:11 lukasbach

This feature would be extremely useful. In fact that's how I thought search would work by default.

@marchingband Would it be possible for you to share the workaround you implemented?

justchaying avatar Dec 15 '23 02:12 justchaying

@justchaying Here is a snippet, I basically manually do a search:

    const handleChangeSearch = useCallback(search=>{
        console.log(search.length)
        setSearchText(search)
        if(!search.length) return
        const files = Object.values(items)
        const children = files.filter(item=>(item.data.name || "").includes(search.toLowerCase())).map(item=>item.index)
        // const parents = files.filter(item=>item.children.some(c=>children.includes(c))).map(item=>item.index)
        const parents = files.filter(item=>hasDescendantIn(item, files, children)).map(item=>item.index)
        setExpandedItems([...expandedItems, ...parents])
    }, [expandedItems, searchText, items])

and update expandedItems in the viewState.

               <ControlledTreeEnvironment
                   ...
                    viewState={{
                        ["tree-1"]: {
                            focusedItem,
                            expandedItems : expandedItems,
                            // expandedItems: [...expandedItems, ...expandedInSearch],
                            selectedItems
                        }
                    }}
                    ...
                >

And I have a custom search bar to do the work

const Search = ({ props, handleChangeSearch, close }) => {
    console.log(props)
    const [text, setText] = useState()
    const dummy = useRef()
    return(
        <div className={'search-input-container'}>
        {/* <div className={'rct-tree-search-input-container'}> */}
            {/* <span className="rct-tree-input-icon" /> */}
            <input 
                {...props} 
                value={text}
                className={'rct-tree-search-input'} 
                onChange={e=>{
                    handleChangeSearch(e.target.value)
                    props.onChange(e)
                    setText(e.target.value)
                }}
                onBlur={()=>{
                    setTimeout(()=>{
                        handleChangeSearch("")
                        props.onBlur()
                    }, 100)
                    close()
                }}
            />
        </div>
    )
}

I don't know if that will help but good luck.

marchingband avatar Dec 20 '23 06:12 marchingband