|  | 
| 1 |  | -import React, { Component } from 'react'; | 
|  | 1 | +import React, { useState, useCallback, useEffect } from 'react'; | 
| 2 | 2 | import CheckboxTree from 'react-checkbox-tree'; | 
| 3 | 3 | 
 | 
| 4 | 4 | import { fileSystem as nodes } from './common.js'; | 
| 5 | 5 | 
 | 
| 6 |  | -class FilterExample extends Component { | 
| 7 |  | -    state = { | 
| 8 |  | -        checked: [ | 
| 9 |  | -            '/app/Http/Controllers/WelcomeController.js', | 
| 10 |  | -            '/app/Http/routes.js', | 
| 11 |  | -            '/public/assets/style.css', | 
| 12 |  | -            '/public/index.html', | 
| 13 |  | -            '/.gitignore', | 
| 14 |  | -        ], | 
| 15 |  | -        expanded: [ | 
| 16 |  | -            '/app', | 
| 17 |  | -        ], | 
| 18 |  | -        filterText: '', | 
| 19 |  | -        filteredNodes: nodes, | 
| 20 |  | -    }; | 
| 21 |  | - | 
| 22 |  | -    constructor(props) { | 
| 23 |  | -        super(props); | 
| 24 |  | - | 
| 25 |  | -        this.onCheck = this.onCheck.bind(this); | 
| 26 |  | -        this.onExpand = this.onExpand.bind(this); | 
| 27 |  | -        this.onFilterChange = this.onFilterChange.bind(this); | 
| 28 |  | -        this.filterTree = this.filterTree.bind(this); | 
| 29 |  | -        this.filterNodes = this.filterNodes.bind(this); | 
| 30 |  | -    } | 
| 31 |  | - | 
| 32 |  | -    onCheck(checked) { | 
| 33 |  | -        this.setState({ checked }); | 
| 34 |  | -    } | 
| 35 |  | - | 
| 36 |  | -    onExpand(expanded) { | 
| 37 |  | -        this.setState({ expanded }); | 
| 38 |  | -    } | 
|  | 6 | +function FilterExample() { | 
|  | 7 | +    const [checked, setChecked] = useState([ | 
|  | 8 | +        '/app/Http/Controllers/WelcomeController.js', | 
|  | 9 | +        '/app/Http/routes.js', | 
|  | 10 | +        '/public/assets/style.css', | 
|  | 11 | +        '/public/index.html', | 
|  | 12 | +        '/.gitignore', | 
|  | 13 | +    ]); | 
|  | 14 | +    const [expanded, setExpanded] = useState(['/app']); | 
|  | 15 | +    const [filterText, setFilterText] = useState(''); | 
|  | 16 | +    const [filteredNodes, setFilteredNodes] = useState(nodes); | 
|  | 17 | + | 
|  | 18 | +    const onCheck = useCallback((checkedValues) => { | 
|  | 19 | +        setChecked(checkedValues); | 
|  | 20 | +    }, []); | 
|  | 21 | + | 
|  | 22 | +    const onExpand = useCallback((expandedValues) => { | 
|  | 23 | +        setExpanded(expandedValues); | 
|  | 24 | +    }, []); | 
|  | 25 | + | 
|  | 26 | +    const onFilterChange = useCallback((e) => { | 
|  | 27 | +        setFilterText(e.target.value); | 
|  | 28 | +    }, []); | 
|  | 29 | + | 
|  | 30 | +    useEffect(() => { | 
|  | 31 | +        const nodeMatchesSearchString = ({ label }) => ( | 
|  | 32 | +            label.toLocaleLowerCase().indexOf(filterText.toLocaleLowerCase()) > -1 | 
|  | 33 | +        ); | 
| 39 | 34 | 
 | 
| 40 |  | -    onFilterChange(e) { | 
| 41 |  | -        this.setState({ filterText: e.target.value }, this.filterTree); | 
| 42 |  | -    } | 
|  | 35 | +        const filterNodes = (filtered, node) => { | 
|  | 36 | +            if (nodeMatchesSearchString(node)) { | 
|  | 37 | +                // Node's label matches the search string | 
|  | 38 | +                filtered.push(node); | 
|  | 39 | +            } else { | 
|  | 40 | +                // Find if any children match the search string or have descendants who do | 
|  | 41 | +                const filteredChildren = (node.children || []).reduce(filterNodes, []); | 
|  | 42 | + | 
|  | 43 | +                // If so, render these children | 
|  | 44 | +                if (filteredChildren.length > 0) { | 
|  | 45 | +                    filtered.push({ ...node, children: filteredChildren }); | 
|  | 46 | +                } | 
|  | 47 | +            } | 
| 43 | 48 | 
 | 
| 44 |  | -    filterTree() { | 
| 45 |  | -        const { filterText } = this.state; | 
|  | 49 | +            return filtered; | 
|  | 50 | +        }; | 
| 46 | 51 | 
 | 
| 47 | 52 |         // Reset nodes back to unfiltered state | 
| 48 | 53 |         if (!filterText) { | 
| 49 |  | -            this.setState({ filteredNodes: nodes }); | 
| 50 |  | - | 
|  | 54 | +            setFilteredNodes(nodes); | 
| 51 | 55 |             return; | 
| 52 | 56 |         } | 
| 53 | 57 | 
 | 
| 54 |  | -        this.setState({ | 
| 55 |  | -            filteredNodes: nodes.reduce(this.filterNodes, []), | 
| 56 |  | -        }); | 
| 57 |  | -    } | 
| 58 |  | - | 
| 59 |  | -    filterNodes(filtered, node) { | 
| 60 |  | -        if (this.nodeMatchesSearchString(node)) { | 
| 61 |  | -            // Node's label matches the search string | 
| 62 |  | -            filtered.push(node); | 
| 63 |  | -        } else { | 
| 64 |  | -            // Find if any children match the search string or have descendants who do | 
| 65 |  | -            const filteredChildren = (node.children || []).reduce(this.filterNodes, []); | 
| 66 |  | - | 
| 67 |  | -            // If so, render these children | 
| 68 |  | -            if (filteredChildren.length > 0) { | 
| 69 |  | -                filtered.push({ ...node, children: filteredChildren }); | 
| 70 |  | -            } | 
| 71 |  | -        } | 
| 72 |  | - | 
| 73 |  | -        return filtered; | 
| 74 |  | -    } | 
| 75 |  | - | 
| 76 |  | -    nodeMatchesSearchString({ label }) { | 
| 77 |  | -        const { filterText } = this.state; | 
| 78 |  | - | 
| 79 |  | -        return label.toLocaleLowerCase().indexOf(filterText.toLocaleLowerCase()) > -1; | 
| 80 |  | -    } | 
| 81 |  | - | 
| 82 |  | -    render() { | 
| 83 |  | -        const { | 
| 84 |  | -            checked, | 
| 85 |  | -            expanded, | 
| 86 |  | -            filterText, | 
| 87 |  | -            filteredNodes, | 
| 88 |  | -        } = this.state; | 
| 89 |  | - | 
| 90 |  | -        return ( | 
| 91 |  | -            <div className="filter-container"> | 
| 92 |  | -                <input | 
| 93 |  | -                    className="filter-text" | 
| 94 |  | -                    placeholder="Search..." | 
| 95 |  | -                    type="text" | 
| 96 |  | -                    value={filterText} | 
| 97 |  | -                    onChange={this.onFilterChange} | 
| 98 |  | -                /> | 
| 99 |  | -                <CheckboxTree | 
| 100 |  | -                    checked={checked} | 
| 101 |  | -                    expanded={expanded} | 
| 102 |  | -                    nodes={filteredNodes} | 
| 103 |  | -                    onCheck={this.onCheck} | 
| 104 |  | -                    onExpand={this.onExpand} | 
| 105 |  | -                /> | 
| 106 |  | -            </div> | 
| 107 |  | -        ); | 
| 108 |  | -    } | 
|  | 58 | +        setFilteredNodes(nodes.reduce(filterNodes, [])); | 
|  | 59 | +    }, [filterText]); | 
|  | 60 | + | 
|  | 61 | +    return ( | 
|  | 62 | +        <div className="filter-container"> | 
|  | 63 | +            <input | 
|  | 64 | +                className="filter-text" | 
|  | 65 | +                placeholder="Search..." | 
|  | 66 | +                type="text" | 
|  | 67 | +                value={filterText} | 
|  | 68 | +                onChange={onFilterChange} | 
|  | 69 | +            /> | 
|  | 70 | +            <CheckboxTree | 
|  | 71 | +                checked={checked} | 
|  | 72 | +                expanded={expanded} | 
|  | 73 | +                nodes={filteredNodes} | 
|  | 74 | +                onCheck={onCheck} | 
|  | 75 | +                onExpand={onExpand} | 
|  | 76 | +            /> | 
|  | 77 | +        </div> | 
|  | 78 | +    ); | 
| 109 | 79 | } | 
| 110 | 80 | 
 | 
| 111 | 81 | export default FilterExample; | 
0 commit comments