Ad

ReactJS+MaterialUI V1 Automatic Nested List Padding

I have nested lists and I don't know how many (recursive function). How to make them to get automatic padding?

The result I get is this

enter image description here:

And I want to get this result:

enter image description here

If I add nested styles, they are the same for all nested lists. I need the padding to increase for each next level.

Can you help? (All comments are welcome!)

The code:

import React from 'react';
import {isNullOrUndefined} from "util";
import {TagNode} from "../interfaces/TagTree";
import HistoryDataUtility from "../../utilities/historyDataUtility";
import TreeUtility from "../../utilities/treeUtility";

import { createStyles } from '@material-ui/core/styles';
import { withStyles } from '@material-ui/core/styles';
import Checkbox from '@material-ui/core/Checkbox';
import Collapse from '@material-ui/core/Collapse';
import Icon from '@material-ui/core/Icon';
import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';
import ListItemIcon from '@material-ui/core/ListItemIcon';
import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction';
import ListItemText from '@material-ui/core/ListItemText';


export interface TreeRendererProps {
    itemList: TagNode[],
    selectedTags: string[],
    expandedFolders: string[],
    onFolderToggle: any,
    onTagToggle: any,
    onSelectedFolderChange?: any,
    selectedFolder?: string,
    classes: {
        root: string;
        nested: string;
    }
}

const styles = createStyles({
    root: {
        width: '100%',
    },
    nested: {
        paddingLeft: 16
    }
});

const TreeRenderer: React.StatelessComponent<TreeRendererProps> = (props) => {


    const buildListItems = (item: TagNode,
                                                    selectedTags: string[],
                                                    onFolderToggle: any,
                                                    onTagToggle: any,
                                                    source: string) => {
        let key = HistoryDataUtility.combineTag(source, item.objId);
        let isExpanded = props.expandedFolders.indexOf(key) != -1;
        let isSelected: boolean = props.selectedFolder === key ? true : false;

        let children1: any, children2: any;

        if(!TreeUtility.isTagItem(item)) {
            children1 = item.children.filter(filterGroups).sort(sortOnTitle).map((child) => {
                                    return buildListItems(child,
                                        selectedTags,
                                        onFolderToggle,
                                        onTagToggle,
                                        source);
                                }) || null;
            children2 = item.children.filter(filterTags).sort(sortOnTitle).map((child) => {
                                    return buildListItems(child,
                                        selectedTags,
                                        onFolderToggle,
                                        onTagToggle,
                                        source);
                                }) || null;
        }

        let collapseItem: any;
        if(isExpanded && !isNullOrUndefined(item.children)) {
            collapseItem =
                <Collapse component="li" in={true} timeout="auto" unmountOnExit>
                    <List disablePadding>
                        {children1}
                        {children2}
                    </List>
                </Collapse>
        }

        let key2 = "list" + Math.random().toString(36).substr(2, 9);

        return (
            !TreeUtility.isTagItem(item)
            ?
                <div key={key2}>
                    <ListItem
                        button key={key}
                        className={props.classes.nested + " with-children"}
                        onClick={onFolderToggle.bind(this, key)}
                        >
                        <ListItemIcon>
                            {isExpanded ? <Icon>remove</Icon> : <Icon>add</Icon>}
                        </ListItemIcon>
                        <ListItemText primary={item.title} />
                        <ListItemSecondaryAction>
                            <Checkbox checked={isSelected} color="primary" onChange={props.onSelectedFolderChange} value={key} />
                        </ListItemSecondaryAction>
                    </ListItem>
                    {collapseItem}
                </div>

            :

                <ListItem
                    button
                    className={props.classes.nested + " no-children" + ((selectedTags.indexOf(key) != -1 || selectedTags.indexOf(item.objId) != -1)    ? " tagitem-activated" : "")}
                    key={key}
                    onClick={onTagToggle.bind(this, key)}
                    >
                    <ListItemIcon><Icon className="status">fiber_manual_record</Icon></ListItemIcon>
                    <ListItemText primary={item.title} />
                </ListItem>
        );
    }

    const filterGroups = (item: TagNode): boolean => {
        return !TreeUtility.isTagItem(item);
    }

    const filterTags = (item: TagNode): boolean => {
        return TreeUtility.isTagItem(item);
    }

    const sortOnTitle = (a: TagNode, b: TagNode) => {
        var nameA = a.title.toUpperCase(); // ignore upper and lowercase
        var nameB = b.title.toUpperCase(); // ignore upper and lowercase
        if (nameA < nameB) {
            return -1;
        }
        if (nameA > nameB) {
            return 1;
        }
        // names must be equal
        return 0;
    }

    const buildList = (items: TagNode[],
                                         selectedTags: string[],
                                         onFolderToggle: any,
                                         onTagToggle: any) => {

        let children1: any, children2: any;

        children1 = items.filter(filterGroups).sort(sortOnTitle).map((item: TagNode) => {
                            let source = item.objId; //Use the top level group nodes id as source
                            return buildListItems(
                                item,
                                selectedTags,
                                onFolderToggle,
                                onTagToggle, source);
                        }) || null;

        children2 = items.filter(filterTags).sort(sortOnTitle).map((item: TagNode) => {
                            return buildListItems(
                                item,
                                selectedTags,
                                onFolderToggle,
                                onTagToggle, null);
                        }) || null;

        return (
             <List>
                {children1}
                {children2}
            </List>
        )
    }

    let tree;

    if (props.itemList.length > 0) {
        if (props.itemList[0].children != undefined) {
            tree = buildList(
                props.itemList[0].children,
                props.selectedTags,
                props.onFolderToggle,
                props.onTagToggle);
        } else {
            tree = <div>{props.itemList[0].name + ' : ' + props.itemList[0].title}</div>
        }
    } else {
        tree = <div><h2 className="small">Model not found.</h2></div>;
    }

    return (<div>{tree}</div>);
}

export default withStyles(styles, { withTheme: true })(TreeRenderer);
Ad

Answer

Actually, the solution is amazingly simple. Just change this line:

<Collapse component="li" in={true} timeout="auto" unmountOnExit>

to this:

<Collapse component="li" in={true} timeout="auto" unmountOnExit style={{paddingLeft: '16px'}}>
Ad
source: stackoverflow.com
Ad