Tree

General


Show Code
import React from "react";
import Tree from 'funda-ui/Tree';
 
// component styles
import 'funda-ui/Tree/index.css';
 
const treeData = [
    {
        title: "Top level 1",
        link: "#",
        slug: 'level-1',
        active: true,
        children: [
            {
                title: "Sub level 1_1",
                link: "#1-1",
                slug: 'level-1_1',
                active: true,
                children: [{
                    title: "Sub Sub Level 1_1",
                    link: "#1-1-1",
                    slug: 'level-1_1_1',
                    children: [{
                        title: "Sub Sub Sub Level 1_1",
                        link: "#1-1-1-1"
                    }]
                }]
            },
            {
                title: "Sub level 1_2",
                link: "#1-2",
                slug: 'level-1_2',
            }]
    },
    {
        title: "Top level 2 (heading)",
        heading: true,
        link: "https://example.com",
        slug: 'level-2',
    },
    {
        title: "Top level 3",
        icon: `<svg height="15px" width="15px" viewBox="0 0 32 32"><path fill="#000" d="M16,1C9.925,1,5,5.925,5,12c0,10,10,19,11,19s11-9,11-19C27,5.925,22.075,1,16,1z M16,16c-2.209,0-4-1.791-4-4c0-2.209,1.791-4,4-4s4,1.791,4,4C20,14.209,18.209,16,16,16z"/></svg>`,
        link: "#",
        slug: 'level-3',
        children: [
            {
                title: "Sub level 3_1",
                link: "#",
                slug: 'level-3_1'
            },
            {
                title: "Sub level 3_2",
                link: "#",
                slug: 'level-3_2'
            },
            {
                title: "Sub level 3_3",
                link: "#",
                slug: 'level-3_3'
            }]
    },
    {
        title: "Top level 4",
        link: "/s",
        slug: 'level-4'
    },
    {
        title: "Top level 5",
        link: "/k",
        slug: 'level-5',
        active: true,
        children: [
            {
                title: "Sub level 5_1",
                link: "/u",
                slug: 'level-5_1'
            }]
    },
    {
        title: "Top level 6",
        link: "/k",
        slug: 'level-6'
    },
    {
        title: "Top level 7",
        link: "#",
        slug: 'level-7'
    }
];
 
export default () => {
 
 
  return (
    <>
 
        <Tree 
            data={treeData} 
            showLine={true}
            onSelect={(e, val) => {
                console.log(val);
                /* 
                {
                    "key": "2",
                    "slug": "5",
                    "link": "#",
                    "optiondata": "{\"title\":\"xxxxx\",\"link\":\"#\",\"selected\":false,\"slug\":5,\"key\":\"2\",\"show\":true}"
                } 
                */
                  
            }} 
        />
 
        <Tree 
            data={treeData} 
            showLine={false}
            disableArrow={true}
            onSelect={(e, val) => {
                console.log(val);
            }} 
        />
 
        <Tree 
            data={treeData} 
            showLine={false}
            disableArrow={true}
            disableCollapse={true}
            onSelect={(e, val) => {
                console.log(val);
            }} 
        />
        
 
        <Tree
            data={treeData}
            showLine={true}
            arrowIcons={[
                <><svg width="1em" height="1em" viewBox="0 0 24 24" fill="none"><path d="M12 16V13M12 13V10M12 13H9M12 13H15M3 6V16.8C3 17.9201 3 18.4798 3.21799 18.9076C3.40973 19.2839 3.71547 19.5905 4.0918 19.7822C4.5192 20 5.07899 20 6.19691 20H17.8031C18.921 20 19.48 20 19.9074 19.7822C20.2837 19.5905 20.5905 19.2841 20.7822 18.9078C21.0002 18.48 21.0002 17.9199 21.0002 16.7998L21.0002 9.19978C21.0002 8.07967 21.0002 7.51962 20.7822 7.0918C20.5905 6.71547 20.2839 6.40973 19.9076 6.21799C19.4798 6 18.9201 6 17.8 6H12M3 6H12M3 6C3 4.89543 3.89543 4 5 4H8.67452C9.1637 4 9.40886 4 9.63904 4.05526C9.84311 4.10425 10.0379 4.18526 10.2168 4.29492C10.4186 4.41857 10.5918 4.59182 10.9375 4.9375L12 6" stroke="#000000" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/></svg></>,
                <><svg width="1em" height="1em" viewBox="0 0 24 24" fill="none">
                <path d="M17.8031 20L19.793 20C20.2255 20 20.4419 20 20.6236 19.9258C20.784 19.8603 20.9251 19.7538 21.0322 19.6175C21.1535 19.4631 21.2132 19.2555 21.332 18.8395L22.4177 15.0396C22.6156 14.3472 22.714 14.0009 22.6363 13.7271C22.5682 13.4871 22.4131 13.2813 22.2011 13.15C21.9731 13.0087 21.64 13.0005 21.0002 13M17.8031 20H6.19691M17.8031 20C18.921 20 19.48 20 19.9074 19.7822C20.2837 19.5905 20.5905 19.2841 20.7822 18.9078C21.0002 18.48 21.0002 17.9199 21.0002 16.7998V13M6.19691 20H5.12109C4.40152 20 4.04075 20 3.79883 19.8501C3.58683 19.7187 3.43144 19.5129 3.36338 19.2729C3.34904 19.2224 3.34073 19.1694 3.33796 19.1122M6.19691 20C5.07899 20 4.5192 20 4.0918 19.7822C3.78624 19.6265 3.52721 19.3952 3.33796 19.1122M21.0002 13C20.9608 13 20.9203 13 20.8786 13H5.00028L3.58301 17.9604L3.58096 17.9674C3.42083 18.5278 3.32578 18.8605 3.33796 19.1122M21.0002 13L21.0002 9.19978C21.0002 8.07967 21.0002 7.51962 20.7822 7.0918C20.5905 6.71547 20.2839 6.40973 19.9076 6.21799C19.4798 6 18.9201 6 17.8 6H12M3.33796 19.1122C3.29413 19.0467 3.25404 18.9784 3.21799 18.9076C3 18.4798 3 17.9201 3 16.8V6M3 6H12M3 6C3 4.89543 3.89543 4 5 4H8.67452C9.1637 4 9.40886 4 9.63904 4.05526C9.84311 4.10425 10.0379 4.18526 10.2168 4.29492C10.4186 4.41857 10.5918 4.59182 10.9375 4.9375L12 6" stroke="#000000" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/></svg></>
            ]}
            onSelect={(e, val) => {
                console.log(val);
            }} 
        />
 
 
    </>
  );
}
 

Asynchronous Usage


change data
Show Code
import React, { useState } from "react";
import Tree from 'funda-ui/Tree';
 
// component styles
import 'funda-ui/Tree/index.css';
 
const treeData = [
    {
        title: "Top level 1",
        link: "#",
        slug: 'level-1'
    },
    {
        title: "Top level 2",
        link: "/s",
        slug: 'level-2',
    },
    {
        title: "Top level 3",
        link: "/k",
        slug: 'level-3',
        active: true,
        children: [
            {
                title: "Sub level 3_1",
                link: "#3-1",
                slug: 'level-3_1'
            },
            {
                title: "Sub level 3_2",
                link: "#3-2",
                slug: 'level-3_2'
            },
            {
                title: "Sub level 3_3",
                link: "#3-3",
                slug: 'level-3_3'
            }]
    }
];
 
 
export default () => {
 
    const [data, setData] = useState<any>(treeData);
   
    function handleClick(e: any) {
        e.preventDefault();
        setData([
            {
                title: "(new) Top level 1",
                link: "#",
                slug: 'level-1-new',
                children: [
                            {
                                title: "Sub level 1_1",
                                link: "#",
                                slug: 'level-1_1-new'
                            }]
            },
            {
                title: "(new)  Top level 2",
                link: "/u",
                slug: 'level-2-new'
            }
        ]);
    }
 
 
    return (
        <>
 
            <a href="#" onClick={handleClick}>change data</a>
            
            <Tree 
                data={data} 
                showLine={true}
                onSelect={(e, val) => {
                    console.log(val);
                }}  
            />
 
 
        </>
    )
}

Load Data Asynchronously

To load data asynchronously when click to expand a treeNode.


Show Code
import React from "react";
import axios from "axios";
 
import Tree from 'funda-ui/Tree';
 
// component styles
import 'funda-ui/Tree/index.css';
 
 
class DataService {
    
    async getList(searchStr = '', limit = 0, otherParam = '') {
 
        console.log('searchStr: ', searchStr);
        console.log("limit: ", limit);
        console.log("otherParam: ", otherParam);
 
        let demoData = [];
 
        // query from "slug"
        if ( otherParam === 'level-2' ) {
 
            demoData = [
                {
                    "parent_id": 0,
                    "id": 1,
                    "item_name": "Title 1",
                    "item_type": "web"
                },
                {
                    "parent_id": 0,
                    "id": 2,
                    "item_name": "Title 2",
                    "item_type": "dev"
                }
            ];   
 
        }
        
        if ( otherParam === 'level-3_1' ) {
 
            demoData = [
                {
                    "parent_id": 0,
                    "id": 1,
                    "item_name": "Title 3",
                    "item_type": "web"
                },
                {
                    "parent_id": 0,
                    "id": 2,
                    "item_name": "Title 4",
                    "item_type": "dev"
                }
            ];   
            
        }
 
 
        return {
            code: 0,
            message: 'OK',
            data: demoData
        };
    }
 
 
    async getListUseAxios(searchStr = '', limit = 0) {
        let _data = null;
        const res = await axios.get(`https://api`, {
            params: {
                s: searchStr,
                limit: limit
            },
            headers: {
                'Authorization': 'Bearer xxxx-xxxxxxxx-xxxxxxxx'
                'Content-Type': 'application/json'
            }
        }).catch(function (error) {
            console.log(error);
        });
 
        if (res && res.status == 200) _data = res.data;
 
 
        // result
        if (_data === null) {
            return {
                code: 0,
                message: 'OK',
                data: []
            };
        } else {
            return {
                code: 0,
                message: 'OK',
                data: _data
            };
        }
 
    }
 
}
 
 
const treeData = [
    {
        title: "Top level 1",
        link: "#",
        slug: 'level-1',
        childrenAsync: true
    },
    {
        title: "Top level 2",
        link: "/s",
        slug: 'level-2',
        childrenAsync: true
    },
    {
        title: "Top level 3",
        link: "/k",
        slug: 'level-3',
        active: true,
        children: [
            {
                title: "Sub level 3_1",
                link: "#3-1",
                slug: 'level-3_1',
                childrenAsync: true
            },
            {
                title: "Sub level 3_2",
                link: "#3-2",
                slug: 'level-3_2'
            },
            {
                title: "Sub level 3_3",
                link: "#3-3",
                slug: 'level-3_3'
            }]
    }
];
 
export default () => {
 
    return (
        <>
 
            <Tree 
                data={treeData} 
                showLine={true}
                onSelect={(e, val) => {
                    console.log(val);
                }} 
                onCollapse={(e, val, updateData) => {
                    console.log(val);
 
                    updateData(val.key, {
                        "fetchFuncAsync": new DataService,
                        "fetchFuncMethod": "getList",
                        "fetchFuncMethodParams": ['', 0, val.slug],
                        "fetchCallback": (res) => {
 
                            // prevent orginal data
                            let placesMap: Record<string, unknown[]> = {};
                            for (const val of res) {
                                placesMap[val.id] = [val.item_name, val.item_type, val.id];
                            }
 
                            //
                            const data = [];
                            for (const key in placesMap) {
                                data.push({
                                    title: placesMap[key][0],
                                    link: "#",
                                    slug: placesMap[key][1]
                                });
                            }
 
                            return data;
 
                        }
                    });
                }} 
            />
 
 
 
        </>
    )
}

Using Checkbox

Add a Checkbox before the tree nodes.


Show Code
import React from "react";
import Tree from 'funda-ui/Tree';
 
// component styles
import 'funda-ui/Tree/index.css';
 
 
const treeData = [
    {
        title: "Top level 1",
        link: "#",
        slug: 'level-1',
        checked: true
    },
    {
        title: "Top level 2",
        link: "/s",
        slug: 'level-2',
        checked: false,
        children: [
            {
                title: "Sub level 2_1",
                link: "#2-1",
                slug: 'level-2_1',
                checked: false
            },
            {
                title: "Sub level 2_2",
                link: "#2-2",
                slug: 'level-2_2',
                checked: false
            }]
    },
    {
        title: "Top level 3",
        link: "/k",
        slug: 'level-3',
        checked: false,
        active: true,
        children: [
            {
                title: "Sub level 3_1",
                link: "#3-1",
                slug: 'level-3_1',
                checked: false
            },
            {
                title: "Sub level 3_2",
                link: "#3-2",
                slug: 'level-3_2',
                checked: false
            },
            {
                title: "Sub level 3_3",
                link: "#3-3",
                slug: 'level-3_3',
                checked: true
            }]
    }
];
 
export default () => {
 
    return (
        <>
 
            <Tree 
                data={treeData} 
                showLine={true}
                checkable={true}
                onCheck={(val) => {
                    console.log(val);
                }} 
                onSelect={(e, val) => {
                    console.log(val);
                }}  
            />
 
 
 
        </>
    )
}

Convert raw data into a tree structure


Show Code
import React, { useEffect, useState } from "react";
import Tree from 'funda-ui/Tree';
 
// component styles
import 'funda-ui/Tree/index.css';
 
 
class DataService {
 
    // `getList()` must be a Promise Object
    async getList(searchStr: string = '', limit: number = 0, otherParam: string = '') {
 
        const demoData = [
            // level 1
            {
                "parent_id": 0,
                "id": 1,
                "item_name": "Title 1",
                "item_type": "web"
            },
            {
                "parent_id": 0,
                "id": 2,
                "item_name": "Title 2",
                "item_type": "dev"
            },
            // level 2
            {
                "parent_id": 1,
                "id": 3,
                "item_name": "Title 3",
                "item_type": "web/ui"
            },
            {
                "parent_id": 1,
                "id": 4,
                "item_name": "Title 4",
                "item_type": "web/ui"
            },
            {
                "parent_id": 2,
                "id": 5,
                "item_name": "Title 5",
                "item_type": "dev"
            },
            // level 3
            {
                "parent_id": 4,
                "id": 6,
                "item_name": "Title 6",
                "item_type": "web/ui/photoshop"
            },  
        ];   
 
        return {
            code: 0,
            message: 'OK',
            data: demoData
        };
    }
 
}
 
 
export default () => {
 
    const [data, setData] = useState<any[]>([]);
 
    /**
     * Convert Tree
     * @param {Array} arr                    - Input array to convert
     * @param  {?String | ?Number} parentId  - Parent id
     * @param  {?String} keyId               - Key value of id.
     * @param  {?String} keyParentId         - Key value of parent id.
     * @returns Array
     */
    function convertTree(arr: any[], parentId: string = '', keyId: string = 'id', keyParentId: string = 'parent_id') {
        
        if( !parentId ) {
            
            // If there is no parent id (when recursing for the first time), all parents will be queried
            return arr.filter((item: any) => !item[keyParentId]).map((item: any) => {
                // Query all child nodes by parent node ID
                item.children = convertTree(arr, item[keyId], keyId, keyParentId);
                return item;
            })
        } else {
            return arr.filter((item: any) => item[keyParentId] === parentId).map((item: any) => {
                // Query all child nodes by parent node ID
                item.children = convertTree(arr, item[keyId], keyId, keyParentId);
                return item;
            })
        }
    }
 
    useEffect(() => {
        new DataService().getList('', 0, '').then((response: any) => {
      
            const _data: any[] = response.data.map((item: any, i: number) => {
                return {
                    id: item.id,
                    parent_id: item.parent_id,
                    active: false,
                    title: `${item.item_name}`,
                    link: "#",
                    slug: item.id
                }
            });
         
 
            const treeData = convertTree(_data);
            setData(treeData);
            
            // do something, such as update `<Scrollbar />`
            // ...
 
        });
    }, []);
 
 
    return (
        <>
 
            <Tree 
                data={data} 
                showLine={true}
                checkable={true}
                onCheck={(val) => {
                    console.log(val);
                }} 
                onSelect={(e, val) => {
                    console.log(val);
                }}  
            />
 
 
 
        </>
    )
}

Push some expansion buttons to each item of the tree

Use those attributes of customContentToHyperlink, customContentToLiTag, itemLinkMouseEnterCallback, itemLinkMouseLeaveCallback, itemMouseEnterCallback, itemMouseLeaveCallback in the data item to create interactions and UI.


Show Code [styles.scss]
/* ---------- Tree Control  ----------- */
.app-tree-item-control {
    position: absolute;
    top: .45rem;
    right: 0;
    z-index: 1;
    background: #fff;
    border-radius: 4px;
    padding: 0 .2rem;
    transform: scale(0);
    transition: .1s ease-in-out;
    transition-delay: 0.25s;
 
 
    &.active {
        transform: scale(1);
    }
 
}
Show Code [index.tsx]
import React, { useEffect, useState, useRef } from "react";
import Tree from 'funda-ui/Tree';
 
// component styles
import 'funda-ui/Tree/index.css';
 
class DataService {
 
    // `getList()` must be a Promise Object
    async getList(searchStr: string = '', limit: number = 0, otherParam: string = '') {
 
        const demoData = [
            // level 1
            {
                "parent_id": 0,
                "id": 1,
                "item_name": "Title 1",
                "item_type": "web"
            },
            {
                "parent_id": 0,
                "id": 2,
                "item_name": "Title 2",
                "item_type": "dev"
            },
            // level 2
            {
                "parent_id": 1,
                "id": 3,
                "item_name": "Title 3",
                "item_type": "web/ui"
            },
            {
                "parent_id": 1,
                "id": 4,
                "item_name": "Title 4",
                "item_type": "web/ui"
            },
            {
                "parent_id": 2,
                "id": 5,
                "item_name": "Title 5",
                "item_type": "dev"
            },
            // level 3
            {
                "parent_id": 4,
                "id": 6,
                "item_name": "Title 6",
                "item_type": "web/ui/photoshop"
            },  
        ];   
 
        return {
            code: 0,
            message: 'OK',
            data: demoData
        };
    }
 
}
 
 
export default () => {
 
    const listRef = useRef<HTMLDivElement>(null);
    const [data, setData] = useState<any[]>([]);
 
    /**
     * Convert Tree
     * @param {Array} arr                    - Input array to convert
     * @param  {?String | ?Number} parentId  - Parent id
     * @param  {?String} keyId               - Key value of id.
     * @param  {?String} keyParentId         - Key value of parent id.
     * @returns Array
     */
    function convertTree(arr: any[], parentId: string = '', keyId: string = 'id', keyParentId: string = 'parent_id') {
        
        if( !parentId ) {
            
            // If there is no parent id (when recursing for the first time), all parents will be queried
            return arr.filter((item: any) => !item[keyParentId]).map((item: any) => {
                // Query all child nodes by parent node ID
                item.children = convertTree(arr, item[keyId], keyId, keyParentId);
                return item;
            })
        } else {
            return arr.filter((item: any) => item[keyParentId] === parentId).map((item: any) => {
                // Query all child nodes by parent node ID
                item.children = convertTree(arr, item[keyId], keyId, keyParentId);
                return item;
            })
        }
    }
 
 
    function handleTreeItemLinkEnter(e: any) {
        e.preventDefault();
        if (listRef.current === null) return;
 
        //
        const slug = e.currentTarget.dataset.slug;
        const controlArea = listRef.current.querySelector(`[data-control-slug="${slug}"]`);
        if (controlArea !== null) {
            controlArea.classList.add('active');
        }
        // root area
        const controlRootArea = listRef.current.querySelector(`[data-control-slug="0"]`);
        if (controlRootArea !== null) {
            controlRootArea.classList.add('active');
        }
 
    }
 
    function handleTreeItemLinkLeave(e: any) {
        if (listRef.current === null) return;
 
        const slug = e.currentTarget.dataset.slug;
        const controlArea = listRef.current.querySelector(`[data-control-slug="${slug}"]`);
        if (controlArea !== null) {
            controlArea.classList.remove('active');
        }
 
    }
 
    useEffect(() => {
        new DataService().getList('', 0, '').then((response: any) => {
      
            const _data: any[] = response.data.map((item: any, i: number) => {
                return {
                    id: item.id,
                    parent_id: item.parent_id,
                    active: false,
                    title: `${item.item_name}`,
                    link: "#",
                    slug: item.id,
                    itemLinkMouseEnterCallback: handleTreeItemLinkEnter,
                    itemLinkMouseLeaveCallback: handleTreeItemLinkLeave,
                    customContentToHyperlink: <>
                        <div className="app-tree-item-control" data-control-slug={item.id}>
                            <a
                                tabIndex={-1}
                                href="#"
                                data-control-slug={item.id}
                                onClick={(e: React.MouseEvent) => {
                                    e.preventDefault();
                                    console.log('item.id: ', (e.currentTarget as any).dataset.controlSlug);
                                }}
                                className="btn btn-info btn-sm text-light ms-2"
                            >
                                Button
                            </a>
    
                        </div>
                    </>,
                }
            });
         
 
            const treeData = convertTree(_data);
            setData(treeData);
            
            // do something, such as update `<Scrollbar />`
            // ...
 
        });
    }, []);
 
 
    return (
        <>
 
            <div ref={listRef}>
 
                <Tree
                    data={data}
                    showLine={true}
                    onSelect={(e, val) => {
                        console.log(val);
                    }}
                />
            </div>
 
 
        </>
    )
}

Live Search Tree

Use the retrieveData attribute to display real-time search content.


Show Code [ListLiveSearch.tsx]
import React, { useState, useEffect, useRef} from "react";
 
interface ListLiveSearchDataConfig {
    title: string | number;
}
 
 
type ListLiveSearchProps = {
    listdata: any[];
    placeholder?: string;
    exClass?: string;
    onChange?: (e: any, data: any[], el: any) => void;
    onSubmit?: (e: any, data: any[], el: any) => void;
};
 
 
 
const ListLiveSearch = (props: ListLiveSearchProps) => {
 
    const {
        listdata,
        placeholder,
        exClass,
        onChange,
        onSubmit
    } = props;
 
    const inputRef = useRef<any>(null);
    const [data, setData] = useState<ListLiveSearchDataConfig[]>([]);
   
    const filterRes = (inputData: any[], val: string) => {
 
        return inputData.filter((item: any) => {
 
            // Avoid fatal errors causing page crashes
            const _val = typeof val !== 'undefined' && val !== null ? val : '';
        
            if (
                (
                    item.title.toLowerCase().indexOf(_val.toLowerCase()) >= 0
                ) &&
                _val != ''
            ) {
                return true;
            } else {
                return false;
            }
        });
    }
 
 
    function handleChange(e: any) {
        if (inputRef.current === null) return;
 
        const _val = e.target.value;
        onChange?.(e, filterRes(data, _val), inputRef.current);
 
        if (_val === '') {
            onSubmit?.(e, filterRes(data, _val), inputRef.current);
        }
 
    }
 
 
    function handleSubmit(e: any) {
        e.preventDefault();
        if (inputRef.current === null) return;
        const _val = inputRef.current.value;
 
        onSubmit?.(e, filterRes(data, _val), inputRef.current);
 
    }
 
 
 
    function handleKeyPressed(e: any) {
        
        if (inputRef.current === null) return;
        const _val = inputRef.current.value;
 
        if (e.keyCode === 13) {
            e.preventDefault();
            onSubmit?.(e, filterRes(data, _val), inputRef.current);
        }
    }
 
    useEffect(() => {
        setData(listdata);
    }, [listdata])
 
    return (
        <>
 
            <div className={`input-group ${exClass || 'mb-1'}`}>
                <input 
                    ref={inputRef} 
                    autoComplete="off" 
                    type="text" 
                    className="form-control" 
                    placeholder={placeholder || ''} 
                    onChange={handleChange} 
                    onKeyDown={handleKeyPressed}
                />
                <div className="input-group-text">
                    <button tabIndex={-1} type="button" className="btn border-end-0 rounded-pill p-0 m-0" onClick={handleSubmit}>
                        <svg width="1em" height="1em" fill="#a5a5a5" viewBox="0 0 16 16">
                            <path d="M12.027 9.92L16 13.95 14 16l-4.075-3.976A6.465 6.465 0 0 1 6.5 13C2.91 13 0 10.083 0 6.5 0 2.91 2.917 0 6.5 0 10.09 0 13 2.917 13 6.5a6.463 6.463 0 0 1-.973 3.42zM1.997 6.452c0 2.48 2.014 4.5 4.5 4.5 2.48 0 4.5-2.015 4.5-4.5 0-2.48-2.015-4.5-4.5-4.5-2.48 0-4.5 2.014-4.5 4.5z" fillRule="evenodd" />
                        </svg>
                    </button>
                </div>
            </div>
 
        </>
    );
 
}
 
 
 
export default ListLiveSearch;
 
Show Code [index.tsx]
import React, { useState, useEffect} from "react";
import Tree from 'funda-ui/Tree';
 
// component styles
import 'funda-ui/Tree/index.css';
 
import ListLiveSearch from './ListLiveSearch';
 
 
const treeData = [
    {
        title: "Top level 1",
        link: "#",
        slug: 'level-1',
        checked: true
    },
    {
        title: "Top level 2",
        link: "/s",
        slug: 'level-2',
        checked: false,
        children: [
            {
                title: "Sub level 2_1",
                link: "#2-1",
                slug: 'level-2_1',
                checked: false
            },
            {
                title: "Sub level 2_2",
                link: "#2-2",
                slug: 'level-2_2',
                checked: false
            }]
    },
    {
        title: "Top level 3",
        link: "/k",
        slug: 'level-3',
        checked: false,
        active: true,
        children: [
            {
                title: "Sub level 3_1",
                link: "#3-1",
                slug: 'level-3_1',
                checked: false
            },
            {
                title: "Sub level 3_2",
                link: "#3-2",
                slug: 'level-3_2',
                checked: false
            },
            {
                title: "Sub level 3_3",
                link: "#3-3",
                slug: 'level-3_3',
                checked: true
            }]
    }
];
 
export default () => {
 
 
    // list live search
    const [flatList, setFlatList] = useState<any[]>([]);
    const [queryData, setQueryData] = useState<any[]>([]);
    function deepClone(obj: any) {
        if (Array.isArray(obj)) {
            return (obj as any).map((item: any) => deepClone(item));
        } else if (typeof obj === 'object' && obj !== null) {
            const clone: any = {};
            for (let key in obj) {
                if (obj.hasOwnProperty(key)) {
                    clone[key] = deepClone(obj[key]);
                }
            }
            return clone;
        } else {
            return obj;
        }
    }
    const flatData = (data: any[]) => {
        const result: any[] = [];
        const iterate = (obj: any) => {
            if (!obj) {
                return;
            }
            obj.forEach((item: any) => {
                result.push(item);
                if (item.children) {
                    iterate(item.children);
                }
 
                // delete current item children
                delete item.children;
 
            })
        }
    
        iterate(data);
        return result;
    };
 
    useEffect(() => {
        setFlatList(flatData(deepClone(treeData)));
    }, []);
 
 
 
    return (
        <>
 
            {/* LIST LIVE SEARCH */}
            <ListLiveSearch 
                listdata={flatList.map((item: any) => {
                    return {
                        title: item.title
                    }
                })}
                placeholder="Search" 
                onChange={(e: any, queryData: any[], input: HTMLInputElement) => {
                    setQueryData(queryData);
                }}
                onSubmit={(e: any, queryData: any[], input: HTMLInputElement) => {
                    setQueryData(queryData);
                }}
            />
            {/* /LIST LIVE SEARCH */}
 
            <Tree 
                retrieveData={queryData}
                data={treeData} 
                showLine={true}
                checkable={true}
                onCheck={(val) => {
                    console.log(val);
                }} 
                onSelect={(e, val) => {
                    console.log(val);
                }}  
            />
 
 
 
        </>
    )
}

API

Tree

import Tree from 'funda-ui/Tree';
PropertyTypeDefaultDescriptionRequired
treeClassNamestringtree-diagram-defaultThe class name of the tree.-
childClassNamestringtree-diagramThe class name of the child on <ul>.-
checkablebooleanfalseSet TreeNode display Checkbox or not.-
showLinebooleanfalseShows a connecting line.-
lineStylestringdotted | dashed | solid | doubleSpecifies what kind of line to display. By default it is "solid".-
alternateCollapsebooleanfalseMutually exclusive alternate expansion between the first levels.-
arrowReactNode<svg width="0.75em" height="0.75em" viewBox="0 0 20 20" fill="none"><path d="M15.795 11.272L7.795 16.272C6.79593 16.8964 5.5 16.1782 5.5 15L5.5 5.00002C5.5 3.82186 6.79593 3.1036 7.795 3.72802L15.795 8.72802C16.735 9.31552 16.735 10.6845 15.795 11.272Z" fill="currentColor"/></svg>Set an arrow of control-
arrowIconsReact.ReactNode[]-Set collapse/expand icon. Use an array to set two icons, if only one is set, the icon animation is activated, and if two are set, the animation is canceled.
such as: [<><svg width="1em" height="1em" viewBox="0 0 24 24">...</svg></>,<><svg width="1em" height="1em" viewBox="0 0 24 24">...</svg></>]
arrow will have no effect when using this attribute.
-
disableArrowbooleanfalseDisable arrow.-
disableCollapsebooleanfalseDisable the collapse effect.-
dataarray-Specify data of Cascading List as a JSON string format. Such as:
[{title:"Top level 1",link:"#",slug:'level-1'},{title:"Top level 2",link:"/s",slug:'level-2',},{title:"Top level 3",link:"https://example.com",slug:'level-3',active:true,children:[{title:"Sub level 3_1",link:"#3-1",slug:'level-3_1'},{title:"Sub level 3_2",link:"#3-2",slug:'level-3_2'},{title:"Sub level 3_3",link:"#3-3",slug:'level-3_3'}]},{title:"Top level 4 (heading)",link:"#",slug:'level-4',}]
-
retrieveDataarray-Specify data of showing items from data. Such as:
[{title:"Top level 1",},{title:"Sub level 2_2"},{title:"Sub level 3_3"}]
It will filter out other options that don't exist, usually used for real-time searches
-
onSelectfunction-Call a function when clicking an item. It returns three callback values.
  1. The first is the current hyperlink
  2. The second is the data (JSON Object)
  3. The third is a callback function, which can initiate an asynchronous request to load children (usage: Please refer to the documentation example)
-
onCollapsefunction-Call a function when collapsing/expanding. The return value is same with onSelect.
When disableArrow is "true", click on the content to trigger it
-
onCheckfunction-Call a function when changing the checkbox. It returns only one callback value (Array).
It is valid when checkable is "true"
-

Array configuration properties of the data:

PropertyTypeDefaultDescriptionRequired
titlestring-The title attribute is used to provide the label text of the hyperlink.
Support html tags
-
linkstring-Specify a URL address.-
activeboolean-Activate current item.-
selectedboolean-Selected current item.-
checkedboolean-Whether the checkbox of the current item is selected.-
headingstring-Is it just plain text. Usually used as a separator.-
iconstring-Specify an icon string, which can be svg or classname.-
slugstring-specify a slug. It can be used for data query parameters-
childrenAsyncboolean-Loading children asynchronously. Set to "true" to trigger loading event.-
childrenarray-Specify a set of sub-navigation, Eg. [{"title":"Sub Sub Level 1","link":"#","slug":"level-1_0","active":true},{"title":"Sub Sub Level 2","link":"#","slug":"level-2_0"}]-
customContentToHyperlinkReact.ReactNode-Add custom content to <a class="nav-link">...</a>-
customContentToLiTagReact.ReactNode-Add custom content to <li class="nav-item">...</li>-
itemLinkMouseEnterCallbackfunction-The mouseenter event of <a class="nav-link"></a>-
itemLinkMouseLeaveCallbackfunction-The mouseleave event of <a class="nav-link"></a>-
itemMouseEnterCallbackfunction-The mouseenter event of <li class="nav-item"></li>-
itemMouseLeaveCallbackfunction-The mouseleave event of <li class="nav-item"></li>-

Array configuration properties of the retrieveData:

PropertyTypeDefaultDescriptionRequired
titlestring-Specify the label text for each option.-

The property list of the last parameter (Function) in the return value of onCollapse and onSelect:

PropertyTypeDefaultDescriptionRequired
keyReact.Key | nullnullMatch the key value of item.-
fetchJSON Object | nullnullSet a request.-
firstRenderbooleanfalseIs it the first render.-

The object properties of fetch:

PropertyTypeDefaultDescriptionRequired
fetchFuncAsyncConstructor-A method as a string from the constructor.-
fetchFuncMethodstring-When the property is true, every time the select changes, a data request will be triggered.
The methord must be a Promise Object.
-
fetchFuncMethodParamsarray-The parameter passed by the method, it is an array.
Note: the first element is a query string, the second element is the number of queried data (usually a number), and then you can increase the third, or fourth, and more parameters.
Such as ['',0], ['',99,'string 1','string 2']
There should be at least one parameter which is the query string.
-
fetchCallbackfunction-Return value from fetchCallback property to format the data of the API callback, which will match the data structure of the component.
At the same time it returns the original data, you will use this function and use the return keyword to return a new value.
Among them, title, link and slug are attributes used by the system, and other attributes can be added freely
-